From 0e270aaba54f55cf39dbca583577fd0cfbd1f43d Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Tue, 30 Sep 2014 17:28:55 +0200 Subject: [PATCH 01/50] - Initial test commit --- .../custom/headersAttrRefersToATableCell.js | 31 +++++++++++++++++++ src/resources/tests.yml | 15 +++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/js/custom/headersAttrRefersToATableCell.js diff --git a/src/js/custom/headersAttrRefersToATableCell.js b/src/js/custom/headersAttrRefersToATableCell.js new file mode 100644 index 000000000..695efd07e --- /dev/null +++ b/src/js/custom/headersAttrRefersToATableCell.js @@ -0,0 +1,31 @@ +quail.headersAttrRefersToATableCell = function( quail, test, Case ) { + + // Table cell headers without referred ids + test.get( '$scope' ).find( 'td, th' ).each( function() { + + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + if ( !$( this ).attr( 'headers' )) { + _case.set( { + 'status': 'inapplicable' + } ); + return; // Stop each loop + } + + if ( $( this ).attr( 'headers' )) { + _case.set( { + 'status': 'passed' + } ); + } else{ + _case.set( { + 'status': 'failed' + } ); + } + + + } ); +}; diff --git a/src/resources/tests.yml b/src/resources/tests.yml index d99b00010..060805c8b 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -1812,6 +1812,21 @@ framesetMustHaveNoFramesSection: - "frame" options: selector: "frameset:not(frameset:has(noframes))" +headersAttrRefersToATableCell: + type: "custom" + testability: 1 + title: + en: "Table cell headers attrtibutes must within the same table have an associated data cell with the same id" + nl: "Tabel cellen met een headers attribuut moeten binnen dezelfde tabel een overeenkomende data cel hebben in het id attribuut dezelfde waarde" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "headers" + - "td" + - "th" + callback: "headersAttrRefersToATableCell" headerH1: type: "headingLevel" testability: 0 From 6ce2cb1c8f10b4c5afba95c695a29da94018498b Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Wed, 1 Oct 2014 10:41:57 +0200 Subject: [PATCH 02/50] - Added tests --- .../custom/headersAttrRefersToATableCell.js | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/js/custom/headersAttrRefersToATableCell.js b/src/js/custom/headersAttrRefersToATableCell.js index 695efd07e..6f351c53e 100644 --- a/src/js/custom/headersAttrRefersToATableCell.js +++ b/src/js/custom/headersAttrRefersToATableCell.js @@ -3,28 +3,34 @@ quail.headersAttrRefersToATableCell = function( quail, test, Case ) { // Table cell headers without referred ids test.get( '$scope' ).find( 'td, th' ).each( function() { - var _case = Case({ - element: this, + var element = this, + _case = Case({ + element: element, expected: $(this).closest('.quail-test').data('expected') }); test.add(_case); - if ( !$( this ).attr( 'headers' )) { + if ( !$( element ).attr( 'headers' )) { _case.set( { 'status': 'inapplicable' } ); - return; // Stop each loop + return; } - if ( $( this ).attr( 'headers' )) { - _case.set( { - 'status': 'passed' - } ); - } else{ - _case.set( { - 'status': 'failed' - } ); - } + var headers = $(element).attr('headers').split(/\s+/); + + $.each( headers, function( index, item ) { + if ( $( element ).closest( 'table' ).find( 'th#' + item + ',td#' + item ).length > 0 ) { + _case.set( { + 'status': 'passed' + } ); + } else { + _case.set( { + 'status': 'failed' + } ); + } + window.console.log($( element ).closest( 'div' ).attr('data-expected'),_case.attributes.expected,_case.attributes.expected); + }); } ); From f8d7ea62e88cf7ddb1ea385552a0407e76fae98c Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Tue, 30 Sep 2014 17:28:55 +0200 Subject: [PATCH 03/50] - Initial test commit --- .../custom/headersAttrRefersToATableCell.js | 31 +++++++++++++++++++ src/resources/tests.yml | 15 +++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/js/custom/headersAttrRefersToATableCell.js diff --git a/src/js/custom/headersAttrRefersToATableCell.js b/src/js/custom/headersAttrRefersToATableCell.js new file mode 100644 index 000000000..695efd07e --- /dev/null +++ b/src/js/custom/headersAttrRefersToATableCell.js @@ -0,0 +1,31 @@ +quail.headersAttrRefersToATableCell = function( quail, test, Case ) { + + // Table cell headers without referred ids + test.get( '$scope' ).find( 'td, th' ).each( function() { + + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + if ( !$( this ).attr( 'headers' )) { + _case.set( { + 'status': 'inapplicable' + } ); + return; // Stop each loop + } + + if ( $( this ).attr( 'headers' )) { + _case.set( { + 'status': 'passed' + } ); + } else{ + _case.set( { + 'status': 'failed' + } ); + } + + + } ); +}; diff --git a/src/resources/tests.yml b/src/resources/tests.yml index d99b00010..060805c8b 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -1812,6 +1812,21 @@ framesetMustHaveNoFramesSection: - "frame" options: selector: "frameset:not(frameset:has(noframes))" +headersAttrRefersToATableCell: + type: "custom" + testability: 1 + title: + en: "Table cell headers attrtibutes must within the same table have an associated data cell with the same id" + nl: "Tabel cellen met een headers attribuut moeten binnen dezelfde tabel een overeenkomende data cel hebben in het id attribuut dezelfde waarde" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "headers" + - "td" + - "th" + callback: "headersAttrRefersToATableCell" headerH1: type: "headingLevel" testability: 0 From 728f9657f5bc4e3bb8aabeba02cd5a9786bc01f3 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Wed, 1 Oct 2014 10:41:57 +0200 Subject: [PATCH 04/50] - Added tests --- .../custom/headersAttrRefersToATableCell.js | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/js/custom/headersAttrRefersToATableCell.js b/src/js/custom/headersAttrRefersToATableCell.js index 695efd07e..6f351c53e 100644 --- a/src/js/custom/headersAttrRefersToATableCell.js +++ b/src/js/custom/headersAttrRefersToATableCell.js @@ -3,28 +3,34 @@ quail.headersAttrRefersToATableCell = function( quail, test, Case ) { // Table cell headers without referred ids test.get( '$scope' ).find( 'td, th' ).each( function() { - var _case = Case({ - element: this, + var element = this, + _case = Case({ + element: element, expected: $(this).closest('.quail-test').data('expected') }); test.add(_case); - if ( !$( this ).attr( 'headers' )) { + if ( !$( element ).attr( 'headers' )) { _case.set( { 'status': 'inapplicable' } ); - return; // Stop each loop + return; } - if ( $( this ).attr( 'headers' )) { - _case.set( { - 'status': 'passed' - } ); - } else{ - _case.set( { - 'status': 'failed' - } ); - } + var headers = $(element).attr('headers').split(/\s+/); + + $.each( headers, function( index, item ) { + if ( $( element ).closest( 'table' ).find( 'th#' + item + ',td#' + item ).length > 0 ) { + _case.set( { + 'status': 'passed' + } ); + } else { + _case.set( { + 'status': 'failed' + } ); + } + window.console.log($( element ).closest( 'div' ).attr('data-expected'),_case.attributes.expected,_case.attributes.expected); + }); } ); From beff3afab2fa07419f3ff1c400badbc7747dac62 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Wed, 1 Oct 2014 13:06:31 +0200 Subject: [PATCH 05/50] - Fixed test --- .../custom/headersAttrRefersToATableCell.js | 49 +++--- .../headersAttrRefersToATableCell.html | 159 ++++++++++-------- 2 files changed, 115 insertions(+), 93 deletions(-) diff --git a/src/js/custom/headersAttrRefersToATableCell.js b/src/js/custom/headersAttrRefersToATableCell.js index 6f351c53e..8064f549a 100644 --- a/src/js/custom/headersAttrRefersToATableCell.js +++ b/src/js/custom/headersAttrRefersToATableCell.js @@ -1,37 +1,38 @@ quail.headersAttrRefersToATableCell = function( quail, test, Case ) { // Table cell headers without referred ids - test.get( '$scope' ).find( 'td, th' ).each( function() { + test.get( '$scope' ).find( 'table' ).each( function() { var element = this, - _case = Case({ - element: element, - expected: $(this).closest('.quail-test').data('expected') - }); - test.add(_case); + _case = Case( { + element: element, + expected: $( this ).closest( '.quail-test' ).data( 'expected' ) + } ); + test.add( _case ); + - if ( !$( element ).attr( 'headers' )) { + if ( $( element ).find( '[headers]' ).length === 0 ) { _case.set( { 'status': 'inapplicable' } ); return; - } - - var headers = $(element).attr('headers').split(/\s+/); - - $.each( headers, function( index, item ) { - if ( $( element ).closest( 'table' ).find( 'th#' + item + ',td#' + item ).length > 0 ) { - _case.set( { - 'status': 'passed' + } else { + $( element ).find( 'th[headers], td[headers]' ).each( function() { + var headers = $( this ).attr( 'headers' ).split( /\s+/ ); + $.each( headers, function( index, item ) { + if (item === "" || $( element ).find( 'th#' + item + ',td#' + item ).length > 0 ) { + _case.set( { + 'status': 'passed' + } ); + return; + } else { + _case.set( { + 'status': 'failed' + } ); + return; + } } ); - } else { - _case.set( { - 'status': 'failed' - } ); - } - window.console.log($( element ).closest( 'div' ).attr('data-expected'),_case.attributes.expected,_case.attributes.expected); - }); - - + } ); + } } ); }; diff --git a/test/accessibility-tests/headersAttrRefersToATableCell.html b/test/accessibility-tests/headersAttrRefersToATableCell.html index fa629b0c4..3a2d22966 100644 --- a/test/accessibility-tests/headersAttrRefersToATableCell.html +++ b/test/accessibility-tests/headersAttrRefersToATableCell.html @@ -1,7 +1,7 @@ - headersAttrRefersToATableCell + headersAttrRefersToATableCell @@ -10,83 +10,104 @@ -
- - - - - - -
-
+
+ + + + + + + + + +
+
-
- - - - -
- -
-
+
+ + + + + + + + + +
+
-
- - - - -
- -
-
+
+ + + + + + + + + +
+
-
- - - - - - +
+ + + + + + + + -
- +
+
-
- - - -
+
+ + + + + +
-
+
-
- - -
-
+
+ + + + +
+
-
- - - - -
-
+
+ + + + + + + +
+
-
- - -
-
+
+ + + + +
+
- + From 91703cc513730d26e94cd41f7992e6ae32df1701 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Tue, 7 Oct 2014 12:11:50 +0200 Subject: [PATCH 06/50] - WIP --- src/js/custom/idrefsHasCorrespondingId.js | 42 +++++++++++++++++++ src/resources/tests.yml | 18 ++++++++ .../linkHasAUniqueContext.html | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/js/custom/idrefsHasCorrespondingId.js diff --git a/src/js/custom/idrefsHasCorrespondingId.js b/src/js/custom/idrefsHasCorrespondingId.js new file mode 100644 index 000000000..5f01feb9b --- /dev/null +++ b/src/js/custom/idrefsHasCorrespondingId.js @@ -0,0 +1,42 @@ +quail.idrefsHasCorrespondingId = function( quail, test, Case ) { + + test.get( '$scope' ).each( function() { + + var $local = $( this ); + var testableElements = $local.find( 'td[headers], th[headers], [aria-controls], [aria-describedby], [aria-flowto], [aria-labelledby], [aria-owns]' ); + + if ( testableElements.length === 0 ) { + window.console.log( "No testable elements in this test" ); + test.add( Case( { + element: this, + expected: $( this ).closest( '.quail-test' ).data( 'expected' ), + status: 'inapplicable' + } ) ); + return; + } + else { + + testableElements.each( function() { + + var _case = test.add( Case( { + element: this, + expected: $( this ).closest( '.quail-test' ).data( 'expected' ) + } ) ); + + if ( $( this ).val() === "poep" ) { + window.console.log( $( this ).val() ); + _case.set( { + 'status': 'passed' + } ); + }else{ + _case.set( { + 'status': 'failed' + } ); + } + } ); + return; + } + + } + ); +}; diff --git a/src/resources/tests.yml b/src/resources/tests.yml index c546ee7f1..c46a655f5 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -2105,6 +2105,24 @@ idRefHasCorrespondingId: techniques: - "F17" callback: "idRefHasCorrespondingId" +idrefsHasCorrespondingId: + type: "custom" + testability: 1 + title: + en: "Elements with an idref attribute must correspond to an element with an ID" + nl: "Elementen met een idref-attribuut moeten corresponderen met een element met een ID" + description: + en: "" + nl: "" + guidelines: + wcag: + 1.3.1: + techniques: + - "F17" + 4.1.1: + techniques: + - "F17" + callback: "idrefsHasCorrespondingId" iIsNotUsed: type: "selector" testability: 1 diff --git a/test/accessibility-tests/linkHasAUniqueContext.html b/test/accessibility-tests/linkHasAUniqueContext.html index e90d4886e..bb6e65047 100644 --- a/test/accessibility-tests/linkHasAUniqueContext.html +++ b/test/accessibility-tests/linkHasAUniqueContext.html @@ -13,7 +13,7 @@ From 65e6fefa16c2364c96afada6529948995fad26b0 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Tue, 7 Oct 2014 13:19:36 +0200 Subject: [PATCH 07/50] - added tests for idrefsHasCorrespondingId --- src/js/custom/idrefsHasCorrespondingId.js | 53 +++++++++++++++-------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/js/custom/idrefsHasCorrespondingId.js b/src/js/custom/idrefsHasCorrespondingId.js index 5f01feb9b..5375e3f65 100644 --- a/src/js/custom/idrefsHasCorrespondingId.js +++ b/src/js/custom/idrefsHasCorrespondingId.js @@ -1,42 +1,59 @@ quail.idrefsHasCorrespondingId = function( quail, test, Case ) { + function getAttribute($element){ + var attribute = []; + var attributeList = ['headers', 'aria-controls', 'aria-describedby', 'aria-flowto', 'aria-labelledby', 'aria-owns']; + + $.each(attributeList, function(index, item){ + + var attr = $element.attr(item); + + if(typeof attr !== typeof undefined && attr !== false){ + attribute = attr; + return; + } + }); + return attribute.split( /\s+/ ); + } + test.get( '$scope' ).each( function() { - var $local = $( this ); - var testableElements = $local.find( 'td[headers], th[headers], [aria-controls], [aria-describedby], [aria-flowto], [aria-labelledby], [aria-owns]' ); + var testableElements = $( this ).find( 'td[headers], th[headers], [aria-controls], [aria-describedby], [aria-flowto], [aria-labelledby], [aria-owns]' ); if ( testableElements.length === 0 ) { - window.console.log( "No testable elements in this test" ); + test.add( Case( { element: this, expected: $( this ).closest( '.quail-test' ).data( 'expected' ), status: 'inapplicable' } ) ); return; - } - else { + } else { testableElements.each( function() { - + var element = this; var _case = test.add( Case( { element: this, expected: $( this ).closest( '.quail-test' ).data( 'expected' ) } ) ); - if ( $( this ).val() === "poep" ) { - window.console.log( $( this ).val() ); - _case.set( { - 'status': 'passed' - } ); - }else{ - _case.set( { - 'status': 'failed' - } ); - } + var attributes = getAttribute($(element)); + var status = 'passed'; + + $.each( attributes, function( index, item ) { + + if ( item !== "" && $( '#' + item ).length === 0 ) { + status = 'failed'; + return; + } + } ); + + _case.set( { + 'status': status + } ); } ); - return; } } ); -}; +}; \ No newline at end of file From d87b4390f1cca193189b08300828777760ce4a26 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Tue, 7 Oct 2014 14:00:29 +0200 Subject: [PATCH 08/50] - Set variable definition on separate lines --- src/js/custom/headersAttrRefersToATableCell.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/custom/headersAttrRefersToATableCell.js b/src/js/custom/headersAttrRefersToATableCell.js index 8064f549a..5c2b5bc4a 100644 --- a/src/js/custom/headersAttrRefersToATableCell.js +++ b/src/js/custom/headersAttrRefersToATableCell.js @@ -3,8 +3,8 @@ quail.headersAttrRefersToATableCell = function( quail, test, Case ) { // Table cell headers without referred ids test.get( '$scope' ).find( 'table' ).each( function() { - var element = this, - _case = Case( { + var element = this; + var _case = Case( { element: element, expected: $( this ).closest( '.quail-test' ).data( 'expected' ) } ); From fde50d9aaab23d94627738ca49db0aacefbc73c8 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Wed, 8 Oct 2014 15:33:19 +0200 Subject: [PATCH 09/50] - Removed :visible from area test, doesn't seem to work --- src/resources/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/tests.yml b/src/resources/tests.yml index f4c39ac1c..2e54867ab 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -558,7 +558,7 @@ areaHasAltValue: - "imagemap" - "content" options: - selector: "area:visible" + selector: "area" test: ":not(area[alt])" areaLinksToSoundFile: type: "selector" From bdfa6be36581c3298c37d88b16ac6bb522bacc38 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Wed, 8 Oct 2014 15:40:33 +0200 Subject: [PATCH 10/50] - Added missing yaml definition for headersAttrRefersToATableCell. Got missing after merge --- src/resources/tests.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/resources/tests.yml b/src/resources/tests.yml index 2e54867ab..f7ef2fc25 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -1829,6 +1829,21 @@ framesetMustHaveNoFramesSection: - "frame" options: selector: "frameset:not(frameset:has(noframes))" +headersAttrRefersToATableCell: + type: "custom" + testability: 1 + title: + en: "Table cell headers attrtibutes must within the same table have an associated data cell with the same id" + nl: "Tabel cellen met een headers attribuut moeten binnen dezelfde tabel een overeenkomende data cel hebben in het id attribuut dezelfde waarde" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "headers" + - "td" + - "th" + callback: "headersAttrRefersToATableCell" headerH1: type: "headingLevel" testability: 0 From 54f5ec53e6918e4c63072cb40ac6c49c5bde9425 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Wed, 8 Oct 2014 16:27:10 +0200 Subject: [PATCH 11/50] - Added inapplicable test for empty scope --- src/js/custom/documentIDsMustBeUnique.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/js/custom/documentIDsMustBeUnique.js b/src/js/custom/documentIDsMustBeUnique.js index d61a62bba..d0a2592cc 100644 --- a/src/js/custom/documentIDsMustBeUnique.js +++ b/src/js/custom/documentIDsMustBeUnique.js @@ -1,14 +1,21 @@ quail.documentIDsMustBeUnique = function(quail, test, Case) { var ids = {}; + + test.get('$scope').each(function(){ + if($(this).children().length === 0) { + test.add(Case({ + element: this, + 'status': 'inapplicable', + expected: $(this).closest('.quail-test').data('expected') + })); + } + }); test.get('$scope').find(':not([id])').each(function() { - var _case = Case({ - element: this - }); - test.add(_case); - _case.set({ + test.add(Case({ + element: this, 'status': 'inapplicable', expected: $(this).closest('.quail-test').data('expected') - }); + })); }); test.get('$scope').find('[id]').each(function() { var _case = Case({ From 548172deb2ef53d1ca2cc85c07bfc19de9675bae Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Thu, 9 Oct 2014 09:52:38 +0200 Subject: [PATCH 12/50] - Added inapplicable test if only 1 element is in the test with an id attribute. --- src/js/custom/documentIDsMustBeUnique.js | 55 ++++++++++++++++-------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/js/custom/documentIDsMustBeUnique.js b/src/js/custom/documentIDsMustBeUnique.js index d0a2592cc..54bb54f14 100644 --- a/src/js/custom/documentIDsMustBeUnique.js +++ b/src/js/custom/documentIDsMustBeUnique.js @@ -1,5 +1,5 @@ quail.documentIDsMustBeUnique = function(quail, test, Case) { - var ids = {}; + test.get('$scope').each(function(){ if($(this).children().length === 0) { @@ -17,24 +17,41 @@ quail.documentIDsMustBeUnique = function(quail, test, Case) { expected: $(this).closest('.quail-test').data('expected') })); }); - test.get('$scope').find('[id]').each(function() { - var _case = Case({ - element: this, - expected: (function (element) { - return quail.components.resolveExpectation(element); - }(this)) - }); - test.add(_case); - if (typeof ids[$(this).attr('id')] === 'undefined') { - _case.set({ - 'status': 'passed' - }); - ids[$(this).attr('id')] = $(this).attr('id'); - } - else { - _case.set({ - 'status': 'failed' + + test.get('$scope').each(function(){ + var ids = {}; + + + $(this).find('[id]').each(function() { + + var _case = Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)) }); - } + test.add(_case); + + window.console.log(ids, typeof ids[$(this).attr('id')], Object.keys(ids).length); + + if(typeof ids[$(this).attr('id')] === 'undefined' && Object.keys(ids).length === 0){ + _case.set({ + 'status': 'inapplicable' + }); + ids[$(this).attr('id')] = $(this).attr('id'); + }else if (typeof ids[$(this).attr('id')] === 'undefined') { + _case.set({ + 'status': 'passed' + }); + ids[$(this).attr('id')] = $(this).attr('id'); + } + else { + _case.set({ + 'status': 'failed' + }); + } + }); + }); + }; From 90623bbff1bda3ff3165b5feadbeddd6da1d3748 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Thu, 9 Oct 2014 09:54:46 +0200 Subject: [PATCH 13/50] - Added inapplicable test if only 1 element is in the test with an id attribute. --- src/js/custom/documentIDsMustBeUnique.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/js/custom/documentIDsMustBeUnique.js b/src/js/custom/documentIDsMustBeUnique.js index 54bb54f14..ca5d5cf2c 100644 --- a/src/js/custom/documentIDsMustBeUnique.js +++ b/src/js/custom/documentIDsMustBeUnique.js @@ -1,6 +1,4 @@ quail.documentIDsMustBeUnique = function(quail, test, Case) { - - test.get('$scope').each(function(){ if($(this).children().length === 0) { test.add(Case({ @@ -17,13 +15,9 @@ quail.documentIDsMustBeUnique = function(quail, test, Case) { expected: $(this).closest('.quail-test').data('expected') })); }); - test.get('$scope').each(function(){ var ids = {}; - - $(this).find('[id]').each(function() { - var _case = Case({ element: this, expected: (function (element) { @@ -31,9 +25,6 @@ quail.documentIDsMustBeUnique = function(quail, test, Case) { }(this)) }); test.add(_case); - - window.console.log(ids, typeof ids[$(this).attr('id')], Object.keys(ids).length); - if(typeof ids[$(this).attr('id')] === 'undefined' && Object.keys(ids).length === 0){ _case.set({ 'status': 'inapplicable' @@ -51,7 +42,5 @@ quail.documentIDsMustBeUnique = function(quail, test, Case) { }); } }); - }); - -}; +}; \ No newline at end of file From 770fd846687e60e6d4bf3e1a0ee658b33a3e3a1d Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Thu, 9 Oct 2014 10:31:50 +0200 Subject: [PATCH 14/50] - Changed test on label on input --- src/js/custom/inputWithoutLabelHasTitle.js | 54 +++++++++++++++------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/js/custom/inputWithoutLabelHasTitle.js b/src/js/custom/inputWithoutLabelHasTitle.js index dae190757..bafb4174e 100644 --- a/src/js/custom/inputWithoutLabelHasTitle.js +++ b/src/js/custom/inputWithoutLabelHasTitle.js @@ -1,21 +1,43 @@ quail.inputWithoutLabelHasTitle = function (quail, test, Case) { - test.get('$scope').find('input, select, textarea').each(function() { - var _case = Case({ - element: this, - expected: $(this).closest('.quail-test').data('expected') - }); - test.add(_case); - if (!$(this).parent('label').length && - !test.get('$scope').find('label[for=' + $(this).attr('id') + ']').length && - (!$(this).attr('title') || quail.isUnreadable($(this).attr('title')))) { - _case.set({ - 'status': 'failed' + + test.get('$scope').each(function(){ + + var testableElements = $(this).find('input, select, textarea'); + + if(testableElements.length === 0){ + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'inapplicable' }); - } - else { - _case.set({ - 'status': 'passed' + test.add(_case); + return; + }else{ + testableElements.each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + if($(this).css('display') === 'none'){ + _case.set({ + 'status': 'inapplicable' + }); + return; + } + if (!test.get('$scope').find('label[for=' + $(this).attr('id') + ']').length && + (!$(this).attr('title') || quail.isUnreadable($(this).attr('title')))) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } }); } }); -}; +}; \ No newline at end of file From 6f88f6bc11b50ac4dc8b884d014a1656a3566473 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 13 Oct 2014 10:23:05 +0200 Subject: [PATCH 15/50] Adjusted spacing --- src/js/lib/earlAssert.js | 78 +++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/src/js/lib/earlAssert.js b/src/js/lib/earlAssert.js index ae4fb0e9d..bcba75b50 100644 --- a/src/js/lib/earlAssert.js +++ b/src/js/lib/earlAssert.js @@ -1,41 +1,45 @@ quail.lib.EarlAssert = (function () { - var resultPrioMap = [ - 'untested', 'inapplicable', 'passed', - 'cantTell', 'failed' - ]; - - function Assert(base) { - var earlAssert = $.extend({}, base, Assert.defaultAssert); - - earlAssert.outcome = $.extend({}, earlAssert.outcome); - - return earlAssert; - } - - Assert.defaultAssert = { - // type: 'assert', - // subject: '', - // assertedBy: '', - // mode: 'automated' - }; - - /** - * Return the priorty index of the result - * @param {result|assert|outcome} val - * @return {integer} Result index in order of prioerty - */ - Assert.getResultPrio = function (val) { - if (typeof val === 'object') { - if (val.outcome) { - val = val.outcome.result; - } else { - val = val.result; - } - } - return resultPrioMap.indexOf(val); - }; - - return Assert; + var resultPrioMap = [ + 'untested', 'inapplicable', 'passed', + 'cantTell', 'failed' + ]; + + /** + * Create a new earl assert object + * @param {object} base An object on which the values of Assert are based + */ + function Assert(base) { + var earlAssert = $.extend({}, base, Assert.defaultAssert); + + earlAssert.outcome = $.extend({}, earlAssert.outcome); + + return earlAssert; + } + + Assert.defaultAssert = { + // type: 'assert', + // subject: '', + // assertedBy: '', + // mode: 'automated' + }; + + /** + * Return the priorty index of the result + * @param {result|assert|outcome} val + * @return {integer} Result index in order of prioerty + */ + Assert.getResultPrio = function (val) { + if (typeof val === 'object') { + if (val.outcome) { + val = val.outcome.result; + } else { + val = val.result; + } + } + return resultPrioMap.indexOf(val); + }; + + return Assert; }()); \ No newline at end of file From 2031cd6e4fab31fa640e762cfa10ac8b511c70c3 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 13 Oct 2014 11:24:21 +0200 Subject: [PATCH 16/50] headersAttrRef performance improvement --- .../custom/headersAttrRefersToATableCell.js | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/js/custom/headersAttrRefersToATableCell.js b/src/js/custom/headersAttrRefersToATableCell.js index 6f351c53e..6248363f1 100644 --- a/src/js/custom/headersAttrRefersToATableCell.js +++ b/src/js/custom/headersAttrRefersToATableCell.js @@ -3,35 +3,32 @@ quail.headersAttrRefersToATableCell = function( quail, test, Case ) { // Table cell headers without referred ids test.get( '$scope' ).find( 'td, th' ).each( function() { - var element = this, + var $this = $(this), _case = Case({ - element: element, - expected: $(this).closest('.quail-test').data('expected') + element: this, + expected: $this.closest('.quail-test').data('expected') }); test.add(_case); - if ( !$( element ).attr( 'headers' )) { + if ( !$this.attr( 'headers' )) { _case.set( { 'status': 'inapplicable' } ); return; } - var headers = $(element).attr('headers').split(/\s+/); - - $.each( headers, function( index, item ) { - if ( $( element ).closest( 'table' ).find( 'th#' + item + ',td#' + item ).length > 0 ) { - _case.set( { + var headers = $this.attr('headers').split(/\s+/); + var table = $this.closest('table'); + $.each(headers, function(index, item) { + if (table.find('th#' + item + ',td#' + item).length > 0) { + _case.set({ 'status': 'passed' - } ); + }); } else { - _case.set( { + _case.set({ 'status': 'failed' - } ); + }); } - window.console.log($( element ).closest( 'div' ).attr('data-expected'),_case.attributes.expected,_case.attributes.expected); }); - - - } ); + }); }; From 201eed23b205c8cb65cf28e8e3376a51a1236baf Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 13 Oct 2014 11:53:19 +0200 Subject: [PATCH 17/50] undid removal of idrefHasCorrespondingId --- src/resources/tests.yml | 18 +++++++++ .../idrefHasCorrespondingId.html | 38 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 test/accessibility-tests/idrefHasCorrespondingId.html diff --git a/src/resources/tests.yml b/src/resources/tests.yml index f7ef2fc25..d3666b83c 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -2116,6 +2116,24 @@ headersUseToMarkSections: - "header" - "content" callback: "headersUseToMarkSections" +idrefHasCorrespondingId: + type: "custom" + testability: 1 + title: + en: "Elements with an idref type attribute must correspond to an element with an ID" + nl: "Elementen met een idref type attribuut moeten corresponderen met een element met een ID" + description: + en: "When using an idref type attribute, the target element with the ID must exist on the page." + nl: "Wanneer je een idref type attibuut gebruikt, moet het doelelement met dit ID ook bestaan op de pagina." + guidelines: + wcag: + 1.3.1: + techniques: + - "F17" + 4.1.1: + techniques: + - "F17" + callback: "idrefHasCorrespondingId" idrefsHasCorrespondingId: type: "custom" testability: 1 diff --git a/test/accessibility-tests/idrefHasCorrespondingId.html b/test/accessibility-tests/idrefHasCorrespondingId.html new file mode 100644 index 000000000..85da718af --- /dev/null +++ b/test/accessibility-tests/idrefHasCorrespondingId.html @@ -0,0 +1,38 @@ + + + + idrefHasCorrespondingId + + + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+
+ + + + \ No newline at end of file From 6623c2be98475c0662273adc802ec0286a9fea90 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 13 Oct 2014 13:38:12 +0200 Subject: [PATCH 18/50] renamed assert to assertion --- src/js/lib/earlAssert.js | 45 -------------------------------- src/js/lib/earlAssertion.js | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 45 deletions(-) delete mode 100644 src/js/lib/earlAssert.js create mode 100644 src/js/lib/earlAssertion.js diff --git a/src/js/lib/earlAssert.js b/src/js/lib/earlAssert.js deleted file mode 100644 index bcba75b50..000000000 --- a/src/js/lib/earlAssert.js +++ /dev/null @@ -1,45 +0,0 @@ -quail.lib.EarlAssert = (function () { - - var resultPrioMap = [ - 'untested', 'inapplicable', 'passed', - 'cantTell', 'failed' - ]; - - /** - * Create a new earl assert object - * @param {object} base An object on which the values of Assert are based - */ - function Assert(base) { - var earlAssert = $.extend({}, base, Assert.defaultAssert); - - earlAssert.outcome = $.extend({}, earlAssert.outcome); - - return earlAssert; - } - - Assert.defaultAssert = { - // type: 'assert', - // subject: '', - // assertedBy: '', - // mode: 'automated' - }; - - /** - * Return the priorty index of the result - * @param {result|assert|outcome} val - * @return {integer} Result index in order of prioerty - */ - Assert.getResultPrio = function (val) { - if (typeof val === 'object') { - if (val.outcome) { - val = val.outcome.result; - } else { - val = val.result; - } - } - return resultPrioMap.indexOf(val); - }; - - return Assert; - -}()); \ No newline at end of file diff --git a/src/js/lib/earlAssertion.js b/src/js/lib/earlAssertion.js new file mode 100644 index 000000000..798aa8d52 --- /dev/null +++ b/src/js/lib/earlAssertion.js @@ -0,0 +1,52 @@ +quail.lib.EarlAssertion = (function () { + var pageUrl; + var resultPriorityMap = [ + 'untested', 'inapplicable', 'passed', + 'cantTell', 'failed' + ]; + + if (window && window.location) { + pageUrl = window.location.href; + } + + /** + * Create a new earl assert object + * @param {object} base An object on which the values of Assert are based + */ + function Assertion(base) { + var earlAssertion = $.extend({}, base, Assertion.defaultAssert); + + earlAssertion.outcome = $.extend({}, earlAssertion.outcome); + + return earlAssertion; + } + + Assertion.defaultAssertion = { + type: 'assertion', + subject: pageUrl, + assertedBy: { + type: 'earl:Software', + name: 'QuailJS' + }, + mode: 'automated' + }; + + /** + * Return the priorty index of the result + * @param {result|assert|outcome} val + * @return {integer} Result index in order of prioerty + */ + Assertion.getResultPriority = function (val) { + if (typeof val === 'object') { + if (val.outcome) { + val = val.outcome.result; + } else { + val = val.result; + } + } + return resultPriorityMap.indexOf(val); + }; + + return Assertion; + +}()); \ No newline at end of file From 5aad484953378bf04c69ce4119970116124f9e02 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 13 Oct 2014 14:08:18 +0200 Subject: [PATCH 19/50] updated refs to earl:assertion --- src/js/lib/wcag/criterion.js | 114 ++++---- src/js/lib/wcag/testClusters.js | 450 ++++++++++++++++---------------- src/resources/tests.yml | 8 +- src/resources/wcag2.yml | 4 +- 4 files changed, 288 insertions(+), 288 deletions(-) diff --git a/src/js/lib/wcag/criterion.js b/src/js/lib/wcag/criterion.js index fb365bad6..44190fe8c 100644 --- a/src/js/lib/wcag/criterion.js +++ b/src/js/lib/wcag/criterion.js @@ -1,70 +1,70 @@ quail.lib.wcag2.Criterion = (function () { - // Provide default values for the assert objects - function aggregateParts(parts, defaultResult) { - var getResultPrio = quail.lib.EarlAssert.getResultPrio, - outcome = { - result: defaultResult - }; - $.each(parts, function (i, part) { - if (getResultPrio(outcome) < getResultPrio(part)) { - outcome.result = part.outcome.result; - } - }); - return outcome; - } + // Provide default values for the assert objects + function aggregateParts(parts, defaultResult) { + var getResultPriority = quail.lib.EarlAssertion.getResultPriority, + outcome = { + result: defaultResult + }; + $.each(parts, function (i, part) { + if (getResultPriority(outcome) < getResultPriority(part)) { + outcome.result = part.outcome.result; + } + }); + return outcome; + } - function constructor (data, testDefinitions) { - var testClusters = [], - criterion = {}, - defaultResult = data['default'] || 'untested', - id = data.id; + function constructor (data, testDefinitions) { + var testClusters = [], + criterion = {}, + defaultResult = data['default'] || 'untested', + id = data.id; - // Create a testCluster object for each cluster (if any) - if ($.isArray(data.testClusters)) { - testClusters = $.map(data.testClusters, function (clusterConf) { - return new quail.lib.wcag2.TestCluster( - clusterConf, testDefinitions - ); - }); - } + // Create a testCluster object for each cluster (if any) + if ($.isArray(data.testClusters)) { + testClusters = $.map(data.testClusters, function (clusterConf) { + return new quail.lib.wcag2.TestCluster( + clusterConf, testDefinitions + ); + }); + } - criterion.getResult = function (data) { - var result, - parts = []; + criterion.getResult = function (data) { + var result, + parts = []; - $.each(testClusters, function (i, cluster) { - var part = cluster.getResults(data); - parts.push.apply(parts, part); - }); - result = new quail.lib.EarlAssert({ - testRequirement: id, - outcome: aggregateParts(parts, defaultResult) - }); - if (parts.length > 0) { - result.hasPart = parts; - } - return result; - }; + $.each(testClusters, function (i, cluster) { + var part = cluster.getResults(data); + parts.push.apply(parts, part); + }); + result = new quail.lib.EarlAssertion({ + testRequirement: id, + outcome: aggregateParts(parts, defaultResult) + }); + if (parts.length > 0) { + result.hasPart = parts; + } + return result; + }; - /** - * Get an array of test used in this criterion - * @param {[json]} criteria - * @return {[array]} - */ - criterion.getTests = function () { - var tests = []; - $.each(testClusters, function (i, cluster) { - tests.push.apply(tests, cluster.tests); - }); - return tests; - }; + /** + * Get an array of test used in this criterion + * @param {[json]} criteria + * @return {[array]} + */ + criterion.getTests = function () { + var tests = []; + $.each(testClusters, function (i, cluster) { + tests.push.apply(tests, cluster.tests); + }); + return tests; + }; - return criterion; - } + return criterion; + } - return constructor; + return constructor; }()); diff --git a/src/js/lib/wcag/testClusters.js b/src/js/lib/wcag/testClusters.js index 699896844..5e75de915 100644 --- a/src/js/lib/wcag/testClusters.js +++ b/src/js/lib/wcag/testClusters.js @@ -1,256 +1,256 @@ quail.lib.wcag2.TestCluster = (function () { - var pointerMap = { - elms: [], - pointers: [], - add: function (testCase) { - var pointer; - if (pointerMap.elms.indexOf(testCase.get('element')) === -1) { - if (testCase.get('html')) { - pointer = [{ - type: 'CharSnippetCompoundPointer', - chars: testCase.get('html'), - CSSSelector: testCase.get('selector') - }]; - } - pointerMap.elms.push(testCase.get('element')); - pointerMap.pointers.push(pointer); - } - }, - getPointer: function (elm) { - var index = pointerMap.elms.indexOf(elm); - return pointerMap.pointers[index]; - } - }; + var pointerMap = { + elms: [], + pointers: [], + add: function (testCase) { + var pointer; + if (pointerMap.elms.indexOf(testCase.get('element')) === -1) { + if (testCase.get('html')) { + pointer = [{ + type: 'CharSnippetCompoundPointer', + chars: testCase.get('html'), + CSSSelector: testCase.get('selector') + }]; + } + pointerMap.elms.push(testCase.get('element')); + pointerMap.pointers.push(pointer); + } + }, + getPointer: function (elm) { + var index = pointerMap.elms.indexOf(elm); + return pointerMap.pointers[index]; + } + }; - /** - * Run the callback for each testcase within the array of tests - * @param {array} tests - * @param {Function} callback Given the parameters (test, testcase) - */ - function eachTestCase(tests, callback) { - $.each(tests, function (i, test) { - test.each(function () { - callback.call(this, test, this); - }); - }); - } + /** + * Run the callback for each testcase within the array of tests + * @param {array} tests + * @param {Function} callback Given the parameters (test, testcase) + */ + function eachTestCase(tests, callback) { + $.each(tests, function (i, test) { + test.each(function () { + callback.call(this, test, this); + }); + }); + } - /** - * Get an array of elements common to all tests provided - * @param {Object} tests - * @return {Array} Array of HTML elements - */ - function getCommonElements(tests) { - var common = [], - map = []; + /** + * Get an array of elements common to all tests provided + * @param {Object} tests + * @return {Array} Array of HTML elements + */ + function getCommonElements(tests) { + var common = [], + map = []; - $.each(tests, function (i, test) { - var elms = []; - test.each(function () { - elms.push(this.get('element')); - pointerMap.add(this); - }); - map.push(elms); - }); - $.each(map, function (i, arr) { - if (i === 0) { - common = arr; - return; - } - var newArr = []; - $.each(arr, function (i, val) { - if (common.indexOf(val) !== -1) { - newArr.push(val); - } - }); - common = newArr; - }); + $.each(tests, function (i, test) { + var elms = []; + test.each(function () { + elms.push(this.get('element')); + pointerMap.add(this); + }); + map.push(elms); + }); + $.each(map, function (i, arr) { + if (i === 0) { + common = arr; + return; + } + var newArr = []; + $.each(arr, function (i, val) { + if (common.indexOf(val) !== -1) { + newArr.push(val); + } + }); + common = newArr; + }); - return common; - } + return common; + } - /** - * Get an array of elements in the given tests - * @param {Object} tests - * @return {Array} Array of HTML elements - */ - function getAllElements(tests) { - var elms = []; - eachTestCase(tests, function (test, testCase) { - var elm = testCase.get('element'); - if (elms.indexOf(elm) === -1) { - elms.push(elm); - pointerMap.add(testCase); - } - }); - return elms; - } + /** + * Get an array of elements in the given tests + * @param {Object} tests + * @return {Array} Array of HTML elements + */ + function getAllElements(tests) { + var elms = []; + eachTestCase(tests, function (test, testCase) { + var elm = testCase.get('element'); + if (elms.indexOf(elm) === -1) { + elms.push(elm); + pointerMap.add(testCase); + } + }); + return elms; + } - /** - * Look at each unique element and create an assert for it - * @param {array[DOM element]} elms - * @param {object} base Base object for the assert - * @return {array[assert]} Array with asserts - */ - function createAssertForEachElement(elms, base) { - var asserts = []; - // Create asserts for each element - $.each(elms, function (i, elm) { - var assert = new quail.lib.EarlAssert(base); - if (elm) { // Don't do undefined pointers - assert.outcome.pointer = pointerMap.getPointer(elm); - } - asserts.push(assert); - }); - return asserts; - } + /** + * Look at each unique element and create an assert for it + * @param {array[DOM element]} elms + * @param {object} base Base object for the assert + * @return {array[assert]} Array with asserts + */ + function createAssertionsForEachElement(elms, base) { + var assertions = []; + // Create asserts for each element + $.each(elms, function (i, elm) { + var assertion = new quail.lib.EarlAssertion(base); + if (elm) { // Don't do undefined pointers + assertion.outcome.pointer = pointerMap.getPointer(elm); + } + assertions.push(assertion); + }); + return assertions; + } - /** - * Combine the test results of a cluster into asserts - * @param {Object} cluster - * @param {Array[Object]} tests - * @return {Array[Object]} Array of Asserts - */ - function getCombinedAsserts(cluster, tests) { - var elms = getCommonElements(tests), - asserts = createAssertForEachElement(elms, { - testCase: cluster.id, - outcome: {result: 'failed'} - }); + /** + * Combine the test results of a cluster into asserts + * @param {Object} cluster + * @param {Array[Object]} tests + * @return {Array[Object]} Array of Asserts + */ + function getCombinedAssertions(cluster, tests) { + var elms = getCommonElements(tests); + var assertions = createAssertionsForEachElement(elms, { + testCase: cluster.id, + outcome: {result: 'failed'} + }); - // Iterate over all results to build the assert - eachTestCase(tests, function (test, testCase) { - // Look up the assert, if any - var newResult = testCase.get('status'), - getResultPrio = quail.lib.EarlAssert.getResultPrio, - assert = asserts[elms.indexOf( - testCase.get('element') - )]; + // Iterate over all results to build the assert + eachTestCase(tests, function (test, testCase) { + // Look up the assert, if any + var newResult = testCase.get('status'); + var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var assertion = assertions[elms.indexOf( + testCase.get('element') + )]; - // Allow the cluster to override the results - if (cluster[newResult]) { - newResult = cluster[newResult]; - } + // Allow the cluster to override the results + if (cluster[newResult]) { + newResult = cluster[newResult]; + } - // Override if the resultId is higher or equal (combine) - if (assert && getResultPrio(assert) >= getResultPrio(newResult)) { - var pointer = assert.outcome.pointer; - assert.outcome = { - result: newResult, - info: test.get('title') - }; - if (pointer) { - assert.outcome.pointer = pointer; - } - } - }); + // Override if the resultId is higher or equal (combine) + if (assertion && getResultPriority(assertion) >= getResultPriority(newResult)) { + var pointer = assertion.outcome.pointer; + assertion.outcome = { + result: newResult, + info: test.get('title') + }; + if (pointer) { + assertion.outcome.pointer = pointer; + } + } + }); - return asserts; - } + return assertions; + } - /** - * Stack the test results of a cluster into asserts - * @param {Object} cluster - * @param {Array[Object]} tests - * @return {Array[Object]} Array of Asserts - */ - function getStackedAsserts(cluster, tests) { - var elms = getAllElements(tests), - asserts = createAssertForEachElement(elms, { - testCase: cluster.id, - outcome: { result: 'untested'} - }); + /** + * Stack the test results of a cluster into asserts + * @param {Object} cluster + * @param {Array[Object]} tests + * @return {Array[Object]} Array of Asserts + */ + function getStackedAssertions(cluster, tests) { + var elms = getAllElements(tests), + asserts = createAssertionsForEachElement(elms, { + testCase: cluster.id, + outcome: { result: 'untested'} + }); - // Iterate over all results to build the assert - eachTestCase(tests, function (test, testCase) { - // Look up the assert, if any - var newResult = testCase.get('status'), - getResultPrio = quail.lib.EarlAssert.getResultPrio, - assert = asserts[elms.indexOf( - testCase.get('element') - )]; + // Iterate over all results to build the assert + eachTestCase(tests, function (test, testCase) { + // Look up the assert, if any + var newResult = testCase.get('status'), + getResultPriority = quail.lib.EarlAssertion.getResultPriority, + assert = asserts[elms.indexOf( + testCase.get('element') + )]; - // Allow the cluster to override the results - if (cluster[newResult]) { - newResult = cluster[newResult]; - } + // Allow the cluster to override the results + if (cluster[newResult]) { + newResult = cluster[newResult]; + } - // Override if the resultId is lower (stacked) - if (assert && getResultPrio(assert) < getResultPrio(newResult)) { - assert.outcome = { - result: newResult, - info: test.get('title') - }; - } - }); - return asserts; - } + // Override if the resultId is lower (stacked) + if (assert && getResultPriority(assert) < getResultPriority(newResult)) { + assert.outcome = { + result: newResult, + info: test.get('title') + }; + } + }); + return asserts; + } - function constructor(config, testDefinitions) { - var cluster = $.extend({ - id: config.tests.join('+') - }, config); + function constructor(config, testDefinitions) { + var cluster = $.extend({ + id: config.tests.join('+') + }, config); - cluster.tests = $.map(cluster.tests, function (test) { - return testDefinitions[test]; - }); + cluster.tests = $.map(cluster.tests, function (test) { + return testDefinitions[test]; + }); - /** - * Filter the data array so it only contains results - * from this cluster - * @param {Array} data - * @return {Array} - */ - cluster.filterDataToTests = function (data) { - var names = $.map(cluster.tests, function (test) { - return test.name; - }), - testData = []; + /** + * Filter the data array so it only contains results + * from this cluster + * @param {Array} data + * @return {Array} + */ + cluster.filterDataToTests = function (data) { + var names = $.map(cluster.tests, function (test) { + return test.name; + }), + testData = []; - $.each(data, function (i, result) { - if (names.indexOf(result.get('name')) !== -1) { - testData.push(result); - } - }); - return testData; - }; + $.each(data, function (i, result) { + if (names.indexOf(result.get('name')) !== -1) { + testData.push(result); + } + }); + return testData; + }; - cluster.getResults = function (tests) { - var asserts, assert; - tests = cluster.filterDataToTests(tests); + cluster.getResults = function (tests) { + var assertions, assertion; + tests = cluster.filterDataToTests(tests); - if (tests.length === 1 || cluster.type === 'combined') { - asserts = getCombinedAsserts(cluster, tests); + if (tests.length === 1 || cluster.type === 'combined') { + assertions = getCombinedAssertions(cluster, tests); - } else if (cluster.type === "stacking") { - asserts = getStackedAsserts(cluster, tests); + } else if (cluster.type === "stacking") { + assertions = getStackedAssertions(cluster, tests); - } else if (window) { - window.console.error( - "Unknown type for cluster " +cluster.id - ); - } + } else if (window) { + window.console.error( + "Unknown type for cluster " +cluster.id + ); + } - // Return a default assert if none was defined - if (asserts) { - if (asserts.length === 0) { - assert = new quail.lib.EarlAssert({ - testCase: cluster.id, - outcome: { - result: 'inapplicable' - } - }), - asserts.push(assert); - } - return asserts; - } - }; + // Return a default assert if none was defined + if (assertions) { + if (assertions.length === 0) { + assertion = new quail.lib.EarlAssertion({ + testCase: cluster.id, + outcome: { + result: 'inapplicable' + } + }), + assertions.push(assertion); + } + return assertions; + } + }; - return cluster; - } + return cluster; + } - return constructor; + return constructor; }()); \ No newline at end of file diff --git a/src/resources/tests.yml b/src/resources/tests.yml index d3666b83c..98a78a228 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -2116,7 +2116,7 @@ headersUseToMarkSections: - "header" - "content" callback: "headersUseToMarkSections" -idrefHasCorrespondingId: +idRefHasCorrespondingId: type: "custom" testability: 1 title: @@ -2133,8 +2133,8 @@ idrefHasCorrespondingId: 4.1.1: techniques: - "F17" - callback: "idrefHasCorrespondingId" -idrefsHasCorrespondingId: + callback: "idRefHasCorrespondingId" +idRefsHasCorrespondingId: type: "custom" testability: 1 title: @@ -2151,7 +2151,7 @@ idrefsHasCorrespondingId: 4.1.1: techniques: - "F17" - callback: "idrefsHasCorrespondingId" + callback: "idRefsHasCorrespondingId" iIsNotUsed: type: "selector" testability: 1 diff --git a/src/resources/wcag2.yml b/src/resources/wcag2.yml index 684ccfd90..b9da5aa21 100644 --- a/src/resources/wcag2.yml +++ b/src/resources/wcag2.yml @@ -318,10 +318,10 @@ failed: "failed" tests: - idRefHasCorrespondingId - # - passed: "passed" # Test not ready + # - passed: "passed" # failed: "failed" # tests: - # - idRefsHasCorrespondingId + # - idrefsHasCorrespondingId # - passed: "passed" # Test not ready # failed: "failed" # tests: From 42b48f83f2f5b34aa7e036aed21e1cc06411c44b Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 13 Oct 2014 14:16:50 +0200 Subject: [PATCH 20/50] added default values to assertion --- src/js/lib/earlAssertion.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/lib/earlAssertion.js b/src/js/lib/earlAssertion.js index 798aa8d52..70971773f 100644 --- a/src/js/lib/earlAssertion.js +++ b/src/js/lib/earlAssertion.js @@ -14,7 +14,7 @@ quail.lib.EarlAssertion = (function () { * @param {object} base An object on which the values of Assert are based */ function Assertion(base) { - var earlAssertion = $.extend({}, base, Assertion.defaultAssert); + var earlAssertion = $.extend({}, base, Assertion.defaultAssertion); earlAssertion.outcome = $.extend({}, earlAssertion.outcome); From 96eee7bd0d1b194f1f3c8e980944984fe0b1e52b Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 13 Oct 2014 17:07:27 +0200 Subject: [PATCH 21/50] Improved code style to better match Quail style --- src/js/lib/earlAssertion.js | 29 ++++---- src/js/lib/wcag/criterion.js | 19 +++-- src/js/lib/wcag/testClusters.js | 118 +++++++++++++++++--------------- src/js/lib/wcag2.js | 112 +++++++++++++++--------------- 4 files changed, 140 insertions(+), 138 deletions(-) diff --git a/src/js/lib/earlAssertion.js b/src/js/lib/earlAssertion.js index 70971773f..2b902fe40 100644 --- a/src/js/lib/earlAssertion.js +++ b/src/js/lib/earlAssertion.js @@ -4,6 +4,16 @@ quail.lib.EarlAssertion = (function () { 'untested', 'inapplicable', 'passed', 'cantTell', 'failed' ]; + var defaultAssertion = { + type: 'assertion', + subject: pageUrl, + assertedBy: { + type: 'earl:Software', + name: 'QuailJS' + }, + mode: 'automated' + }; + if (window && window.location) { pageUrl = window.location.href; @@ -11,25 +21,14 @@ quail.lib.EarlAssertion = (function () { /** * Create a new earl assert object - * @param {object} base An object on which the values of Assert are based + * @param {object} base Properties from this object are added to the Assertion + * and override the default. */ function Assertion(base) { - var earlAssertion = $.extend({}, base, Assertion.defaultAssertion); - - earlAssertion.outcome = $.extend({}, earlAssertion.outcome); - - return earlAssertion; + $.extend(this, base, defaultAssertion); + this.outcome = $.extend({}, this.outcome); } - Assertion.defaultAssertion = { - type: 'assertion', - subject: pageUrl, - assertedBy: { - type: 'earl:Software', - name: 'QuailJS' - }, - mode: 'automated' - }; /** * Return the priorty index of the result diff --git a/src/js/lib/wcag/criterion.js b/src/js/lib/wcag/criterion.js index 44190fe8c..2a498ebd4 100644 --- a/src/js/lib/wcag/criterion.js +++ b/src/js/lib/wcag/criterion.js @@ -2,10 +2,9 @@ quail.lib.wcag2.Criterion = (function () { // Provide default values for the assert objects function aggregateParts(parts, defaultResult) { - var getResultPriority = quail.lib.EarlAssertion.getResultPriority, - outcome = { - result: defaultResult - }; + var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var outcome = {result: defaultResult}; + $.each(parts, function (i, part) { if (getResultPriority(outcome) < getResultPriority(part)) { outcome.result = part.outcome.result; @@ -16,10 +15,10 @@ quail.lib.wcag2.Criterion = (function () { function constructor (data, testDefinitions) { - var testClusters = [], - criterion = {}, - defaultResult = data['default'] || 'untested', - id = data.id; + var testClusters = []; + var criterion = {}; + var defaultResult = data['default'] || 'untested'; + var id = data.id; // Create a testCluster object for each cluster (if any) if ($.isArray(data.testClusters)) { @@ -32,8 +31,8 @@ quail.lib.wcag2.Criterion = (function () { criterion.getResult = function (data) { - var result, - parts = []; + var result; + var parts = []; $.each(testClusters, function (i, cluster) { var part = cluster.getResults(data); diff --git a/src/js/lib/wcag/testClusters.js b/src/js/lib/wcag/testClusters.js index 5e75de915..51aa4f953 100644 --- a/src/js/lib/wcag/testClusters.js +++ b/src/js/lib/wcag/testClusters.js @@ -42,8 +42,8 @@ quail.lib.wcag2.TestCluster = (function () { * @return {Array} Array of HTML elements */ function getCommonElements(tests) { - var common = [], - map = []; + var common = []; + var map = []; $.each(tests, function (i, test) { var elms = []; @@ -157,8 +157,8 @@ quail.lib.wcag2.TestCluster = (function () { * @return {Array[Object]} Array of Asserts */ function getStackedAssertions(cluster, tests) { - var elms = getAllElements(tests), - asserts = createAssertionsForEachElement(elms, { + var elms = getAllElements(tests); + var asserts = createAssertionsForEachElement(elms, { testCase: cluster.id, outcome: { result: 'untested'} }); @@ -166,9 +166,9 @@ quail.lib.wcag2.TestCluster = (function () { // Iterate over all results to build the assert eachTestCase(tests, function (test, testCase) { // Look up the assert, if any - var newResult = testCase.get('status'), - getResultPriority = quail.lib.EarlAssertion.getResultPriority, - assert = asserts[elms.indexOf( + var newResult = testCase.get('status'); + var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var assert = asserts[elms.indexOf( testCase.get('element') )]; @@ -189,68 +189,72 @@ quail.lib.wcag2.TestCluster = (function () { } - function constructor(config, testDefinitions) { - var cluster = $.extend({ + function TestCluster(config, testDefinitions) { + $.extend(this, { id: config.tests.join('+') }, config); - cluster.tests = $.map(cluster.tests, function (test) { + this.tests = $.map(this.tests, function (test) { return testDefinitions[test]; }); + } - /** - * Filter the data array so it only contains results - * from this cluster - * @param {Array} data - * @return {Array} - */ - cluster.filterDataToTests = function (data) { - var names = $.map(cluster.tests, function (test) { - return test.name; - }), - testData = []; - $.each(data, function (i, result) { - if (names.indexOf(result.get('name')) !== -1) { - testData.push(result); - } - }); - return testData; - }; + /** + * Filter the data array so it only contains results + * from this cluster + * @param {Array} data + * @return {Array} + */ + TestCluster.prototype.filterDataToTests = function (data) { + var names = $.map(this.tests, function (test) { + return test.name; + }); + var testData = []; - cluster.getResults = function (tests) { - var assertions, assertion; - tests = cluster.filterDataToTests(tests); + $.each(data, function (i, result) { + if (names.indexOf(result.get('name')) !== -1) { + testData.push(result); + } + }); + return testData; + }; - if (tests.length === 1 || cluster.type === 'combined') { - assertions = getCombinedAssertions(cluster, tests); + /** + * Get the results of this test cluster based on the tests provided for it + * @param {object} tests As provided by Quail + * @return {object} EARL assertions + */ + TestCluster.prototype.getResults = function (tests) { + var assertions, assertion; + tests = this.filterDataToTests(tests); - } else if (cluster.type === "stacking") { - assertions = getStackedAssertions(cluster, tests); + if (tests.length === 1 || this.type === 'combined') { + assertions = getCombinedAssertions(this, tests); - } else if (window) { - window.console.error( - "Unknown type for cluster " +cluster.id - ); - } + } else if (this.type === "stacking") { + assertions = getStackedAssertions(this, tests); - // Return a default assert if none was defined - if (assertions) { - if (assertions.length === 0) { - assertion = new quail.lib.EarlAssertion({ - testCase: cluster.id, - outcome: { - result: 'inapplicable' - } - }), - assertions.push(assertion); - } - return assertions; - } - }; + } else if (window) { + window.console.error( + "Unknown type for cluster " + this.id + ); + } - return cluster; - } + // Return a default assert if none was defined + if (assertions) { + if (assertions.length === 0) { + assertion = new quail.lib.EarlAssertion({ + testCase: this.id, + outcome: { + result: 'inapplicable' + } + }), + assertions.push(assertion); + } + return assertions; + } + }; - return constructor; + return TestCluster; }()); \ No newline at end of file diff --git a/src/js/lib/wcag2.js b/src/js/lib/wcag2.js index 9e17fe2cb..4e6a63402 100644 --- a/src/js/lib/wcag2.js +++ b/src/js/lib/wcag2.js @@ -1,72 +1,72 @@ /* A logical combo of Techniques and the intersection of their outcomes. */ quail.lib.wcag2 = (function () { - 'use strict'; - var ajaxOpt = {async: false, dataType: 'json'}; + 'use strict'; + var ajaxOpt = {async: false, dataType: 'json'}; - /** - * Run Quail using WCAG 2 - * @param {[object]} options Quail options - */ - function runWCAG20Quail(options) { + /** + * Run Quail using WCAG 2 + * @param {[object]} options Quail options + */ + function runWCAG20Quail(options) { - // Load the required json files - $.when( - $.ajax(options.jsonPath + '/wcag2.json', ajaxOpt), - $.ajax(options.jsonPath + '/tests.json', ajaxOpt)) + // Load the required json files + $.when( + $.ajax(options.jsonPath + '/wcag2.json', ajaxOpt), + $.ajax(options.jsonPath + '/tests.json', ajaxOpt)) - // Setup quail given the tests described in the json files - .done(function (wcag2Call, testsCall) { - var criteria, accessibilityTests, knownTests, - allTests = []; + // Setup quail given the tests described in the json files + .done(function (wcag2Call, testsCall) { + var criteria, accessibilityTests, knownTests; + var allTests = []; - criteria = $.map(wcag2Call[0], function (critData) { - return new quail.lib.wcag2.Criterion( - critData, testsCall[0]); - }); + criteria = $.map(wcag2Call[0], function (critData) { + return new quail.lib.wcag2.Criterion( + critData, testsCall[0]); + }); - // Create the accessibiliyTests object, based on the - // tests in the criteria - $.each(criteria, function (i, criterion) { - allTests.push.apply(allTests, criterion.getTests()); - }); + // Create the accessibiliyTests object, based on the + // tests in the criteria + $.each(criteria, function (i, criterion) { + allTests.push.apply(allTests, criterion.getTests()); + }); - knownTests = []; - accessibilityTests = []; + knownTests = []; + accessibilityTests = []; - // Remove duplicates - // TODO: Figure out why some tests are created multiple times - $.each(allTests, function (i, test) { - if (knownTests.indexOf(test.title.en) === -1) { - knownTests.push(test.title.en); - accessibilityTests.push(test); - } - }); + // Remove duplicates + // TODO: Figure out why some tests are created multiple times + $.each(allTests, function (i, test) { + if (knownTests.indexOf(test.title.en) === -1) { + knownTests.push(test.title.en); + accessibilityTests.push(test); + } + }); - // Run quail with the tests instead of the guideline - $(quail.html).quail({ - accessibilityTests: accessibilityTests, - // Have wcag2 intercept the callback - testCollectionComplete: createCallback( - criteria, options.testCollectionComplete) - }); - }); - } + // Run quail with the tests instead of the guideline + $(quail.html).quail({ + accessibilityTests: accessibilityTests, + // Have wcag2 intercept the callback + testCollectionComplete: createCallback( + criteria, options.testCollectionComplete) + }); + }); + } - function createCallback(criteria, callback) { - return function (status, data) { - if (status === 'complete') { - data = $.map(criteria, function (criterion) { - return criterion.getResult(data); - }); - } + function createCallback(criteria, callback) { + return function (status, data) { + if (status === 'complete') { + data = $.map(criteria, function (criterion) { + return criterion.getResult(data); + }); + } - callback(status, data); - }; - } + callback(status, data); + }; + } - return { - run: runWCAG20Quail - }; + return { + run: runWCAG20Quail + }; }()); \ No newline at end of file From 6d52ac989b0847d59725aed1449d8b4a521a6738 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Mon, 13 Oct 2014 17:08:00 +0200 Subject: [PATCH 22/50] Added new tests for WCAG 2 mapping --- src/resources/tests.yml | 4 ++-- src/resources/wcag2.yml | 32 +++++++++++++++----------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/resources/tests.yml b/src/resources/tests.yml index 98a78a228..4c4dc7770 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -2134,7 +2134,7 @@ idRefHasCorrespondingId: techniques: - "F17" callback: "idRefHasCorrespondingId" -idRefsHasCorrespondingId: +idrefsHasCorrespondingId: type: "custom" testability: 1 title: @@ -2151,7 +2151,7 @@ idRefsHasCorrespondingId: 4.1.1: techniques: - "F17" - callback: "idRefsHasCorrespondingId" + callback: "idrefsHasCorrespondingId" iIsNotUsed: type: "selector" testability: 1 diff --git a/src/resources/wcag2.yml b/src/resources/wcag2.yml index b9da5aa21..b70a4707b 100644 --- a/src/resources/wcag2.yml +++ b/src/resources/wcag2.yml @@ -135,11 +135,12 @@ 1.4.1: id: "wcag20:visual-audio-contrast-without-color" level: "wcag20:level_a" + default: "cantTell" testClusters: - # - passed: "passed" # Test doesn't yet exist - # failed: "failed" - # tests: - # - aInPhasADestinctStyle + - passed: "passed" + failed: "failed" + tests: + - aInPHasADistinctStyle 1.4.2: id: "wcag20:visual-audio-contrast-dis-audio" level: "wcag20:level_a" @@ -151,10 +152,6 @@ failed: failed tests: - cssTextHasContrast - # - passed: "passed" # Test doesn't yet exist - # failed: "failed" - # tests: - # - foreAndBackgroundAreDefinedTogether 1.4.4: id: "wcag20:visual-audio-contrast-scale" level: "wcag20:level_aa" @@ -215,11 +212,12 @@ 2.4.4: id: "wcag20:navigation-mechanisms-refs" level: "wcag20:level_a" + # default: "cantTell" testClusters: # - passed: "passed" # Test doesn't yet exist # failed: "failed" # tests: - # - linkHaveAUniqueContext + # - linkHasAUniqueContext 2.4.5: id: "wcag20:navigation-mechanisms-mult-loc" level: "wcag20:level_aa" @@ -318,10 +316,10 @@ failed: "failed" tests: - idRefHasCorrespondingId - # - passed: "passed" - # failed: "failed" - # tests: - # - idrefsHasCorrespondingId + - passed: "passed" + failed: "failed" + tests: + - idrefsHasCorrespondingId # - passed: "passed" # Test not ready # failed: "failed" # tests: @@ -404,10 +402,10 @@ failed: "failed" tests: - aMustContainText - # - passed: "passed" # this test should be fixed - # failed: "failed" - # tests: - # - buttonHasValue + - passed: "passed" + failed: "failed" + tests: + - buttonHasName # - passed: "passed" # This test doesn't exist yet # failed: "failed" # tests: From 5e712ff82cc792f2150ec47048a29798c39d1c97 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Wed, 15 Oct 2014 12:54:59 +0200 Subject: [PATCH 23/50] - Setup precondition tests --- src/js/lib/wcag2.js | 1 + src/js/lib/wcag2/criterion.js | 69 ++++++++ src/js/lib/wcag2/earlAssertion.js | 51 ++++++ src/js/lib/wcag2/testClusters.js | 260 ++++++++++++++++++++++++++++++ src/resources/preconditions.yml | 56 +++++++ src/resources/wcag2.yml | 19 +++ 6 files changed, 456 insertions(+) create mode 100644 src/js/lib/wcag2/criterion.js create mode 100644 src/js/lib/wcag2/earlAssertion.js create mode 100644 src/js/lib/wcag2/testClusters.js create mode 100644 src/resources/preconditions.yml diff --git a/src/js/lib/wcag2.js b/src/js/lib/wcag2.js index 4e6a63402..8155894f5 100644 --- a/src/js/lib/wcag2.js +++ b/src/js/lib/wcag2.js @@ -12,6 +12,7 @@ quail.lib.wcag2 = (function () { // Load the required json files $.when( $.ajax(options.jsonPath + '/wcag2.json', ajaxOpt), + $.ajax(options.jsonPath + '/preconditions.json', ajaxOpt), $.ajax(options.jsonPath + '/tests.json', ajaxOpt)) // Setup quail given the tests described in the json files diff --git a/src/js/lib/wcag2/criterion.js b/src/js/lib/wcag2/criterion.js new file mode 100644 index 000000000..2a498ebd4 --- /dev/null +++ b/src/js/lib/wcag2/criterion.js @@ -0,0 +1,69 @@ +quail.lib.wcag2.Criterion = (function () { + + // Provide default values for the assert objects + function aggregateParts(parts, defaultResult) { + var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var outcome = {result: defaultResult}; + + $.each(parts, function (i, part) { + if (getResultPriority(outcome) < getResultPriority(part)) { + outcome.result = part.outcome.result; + } + }); + return outcome; + } + + + function constructor (data, testDefinitions) { + var testClusters = []; + var criterion = {}; + var defaultResult = data['default'] || 'untested'; + var id = data.id; + + // Create a testCluster object for each cluster (if any) + if ($.isArray(data.testClusters)) { + testClusters = $.map(data.testClusters, function (clusterConf) { + return new quail.lib.wcag2.TestCluster( + clusterConf, testDefinitions + ); + }); + } + + + criterion.getResult = function (data) { + var result; + var parts = []; + + $.each(testClusters, function (i, cluster) { + var part = cluster.getResults(data); + parts.push.apply(parts, part); + }); + result = new quail.lib.EarlAssertion({ + testRequirement: id, + outcome: aggregateParts(parts, defaultResult) + }); + if (parts.length > 0) { + result.hasPart = parts; + } + return result; + }; + + /** + * Get an array of test used in this criterion + * @param {[json]} criteria + * @return {[array]} + */ + criterion.getTests = function () { + var tests = []; + $.each(testClusters, function (i, cluster) { + tests.push.apply(tests, cluster.tests); + }); + return tests; + }; + + return criterion; + } + + return constructor; + +}()); diff --git a/src/js/lib/wcag2/earlAssertion.js b/src/js/lib/wcag2/earlAssertion.js new file mode 100644 index 000000000..2b902fe40 --- /dev/null +++ b/src/js/lib/wcag2/earlAssertion.js @@ -0,0 +1,51 @@ +quail.lib.EarlAssertion = (function () { + var pageUrl; + var resultPriorityMap = [ + 'untested', 'inapplicable', 'passed', + 'cantTell', 'failed' + ]; + var defaultAssertion = { + type: 'assertion', + subject: pageUrl, + assertedBy: { + type: 'earl:Software', + name: 'QuailJS' + }, + mode: 'automated' + }; + + + if (window && window.location) { + pageUrl = window.location.href; + } + + /** + * Create a new earl assert object + * @param {object} base Properties from this object are added to the Assertion + * and override the default. + */ + function Assertion(base) { + $.extend(this, base, defaultAssertion); + this.outcome = $.extend({}, this.outcome); + } + + + /** + * Return the priorty index of the result + * @param {result|assert|outcome} val + * @return {integer} Result index in order of prioerty + */ + Assertion.getResultPriority = function (val) { + if (typeof val === 'object') { + if (val.outcome) { + val = val.outcome.result; + } else { + val = val.result; + } + } + return resultPriorityMap.indexOf(val); + }; + + return Assertion; + +}()); \ No newline at end of file diff --git a/src/js/lib/wcag2/testClusters.js b/src/js/lib/wcag2/testClusters.js new file mode 100644 index 000000000..51aa4f953 --- /dev/null +++ b/src/js/lib/wcag2/testClusters.js @@ -0,0 +1,260 @@ +quail.lib.wcag2.TestCluster = (function () { + + var pointerMap = { + elms: [], + pointers: [], + add: function (testCase) { + var pointer; + if (pointerMap.elms.indexOf(testCase.get('element')) === -1) { + if (testCase.get('html')) { + pointer = [{ + type: 'CharSnippetCompoundPointer', + chars: testCase.get('html'), + CSSSelector: testCase.get('selector') + }]; + } + pointerMap.elms.push(testCase.get('element')); + pointerMap.pointers.push(pointer); + } + }, + getPointer: function (elm) { + var index = pointerMap.elms.indexOf(elm); + return pointerMap.pointers[index]; + } + }; + + /** + * Run the callback for each testcase within the array of tests + * @param {array} tests + * @param {Function} callback Given the parameters (test, testcase) + */ + function eachTestCase(tests, callback) { + $.each(tests, function (i, test) { + test.each(function () { + callback.call(this, test, this); + }); + }); + } + + /** + * Get an array of elements common to all tests provided + * @param {Object} tests + * @return {Array} Array of HTML elements + */ + function getCommonElements(tests) { + var common = []; + var map = []; + + $.each(tests, function (i, test) { + var elms = []; + test.each(function () { + elms.push(this.get('element')); + pointerMap.add(this); + }); + map.push(elms); + }); + $.each(map, function (i, arr) { + if (i === 0) { + common = arr; + return; + } + var newArr = []; + $.each(arr, function (i, val) { + if (common.indexOf(val) !== -1) { + newArr.push(val); + } + }); + common = newArr; + }); + + return common; + } + + /** + * Get an array of elements in the given tests + * @param {Object} tests + * @return {Array} Array of HTML elements + */ + function getAllElements(tests) { + var elms = []; + eachTestCase(tests, function (test, testCase) { + var elm = testCase.get('element'); + if (elms.indexOf(elm) === -1) { + elms.push(elm); + pointerMap.add(testCase); + } + }); + return elms; + } + + /** + * Look at each unique element and create an assert for it + * @param {array[DOM element]} elms + * @param {object} base Base object for the assert + * @return {array[assert]} Array with asserts + */ + function createAssertionsForEachElement(elms, base) { + var assertions = []; + // Create asserts for each element + $.each(elms, function (i, elm) { + var assertion = new quail.lib.EarlAssertion(base); + if (elm) { // Don't do undefined pointers + assertion.outcome.pointer = pointerMap.getPointer(elm); + } + assertions.push(assertion); + }); + return assertions; + } + + /** + * Combine the test results of a cluster into asserts + * @param {Object} cluster + * @param {Array[Object]} tests + * @return {Array[Object]} Array of Asserts + */ + function getCombinedAssertions(cluster, tests) { + var elms = getCommonElements(tests); + var assertions = createAssertionsForEachElement(elms, { + testCase: cluster.id, + outcome: {result: 'failed'} + }); + + // Iterate over all results to build the assert + eachTestCase(tests, function (test, testCase) { + // Look up the assert, if any + var newResult = testCase.get('status'); + var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var assertion = assertions[elms.indexOf( + testCase.get('element') + )]; + + // Allow the cluster to override the results + if (cluster[newResult]) { + newResult = cluster[newResult]; + } + + // Override if the resultId is higher or equal (combine) + if (assertion && getResultPriority(assertion) >= getResultPriority(newResult)) { + var pointer = assertion.outcome.pointer; + assertion.outcome = { + result: newResult, + info: test.get('title') + }; + if (pointer) { + assertion.outcome.pointer = pointer; + } + } + }); + + return assertions; + } + + + /** + * Stack the test results of a cluster into asserts + * @param {Object} cluster + * @param {Array[Object]} tests + * @return {Array[Object]} Array of Asserts + */ + function getStackedAssertions(cluster, tests) { + var elms = getAllElements(tests); + var asserts = createAssertionsForEachElement(elms, { + testCase: cluster.id, + outcome: { result: 'untested'} + }); + + // Iterate over all results to build the assert + eachTestCase(tests, function (test, testCase) { + // Look up the assert, if any + var newResult = testCase.get('status'); + var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var assert = asserts[elms.indexOf( + testCase.get('element') + )]; + + // Allow the cluster to override the results + if (cluster[newResult]) { + newResult = cluster[newResult]; + } + + // Override if the resultId is lower (stacked) + if (assert && getResultPriority(assert) < getResultPriority(newResult)) { + assert.outcome = { + result: newResult, + info: test.get('title') + }; + } + }); + return asserts; + } + + + function TestCluster(config, testDefinitions) { + $.extend(this, { + id: config.tests.join('+') + }, config); + + this.tests = $.map(this.tests, function (test) { + return testDefinitions[test]; + }); + } + + + /** + * Filter the data array so it only contains results + * from this cluster + * @param {Array} data + * @return {Array} + */ + TestCluster.prototype.filterDataToTests = function (data) { + var names = $.map(this.tests, function (test) { + return test.name; + }); + var testData = []; + + $.each(data, function (i, result) { + if (names.indexOf(result.get('name')) !== -1) { + testData.push(result); + } + }); + return testData; + }; + + /** + * Get the results of this test cluster based on the tests provided for it + * @param {object} tests As provided by Quail + * @return {object} EARL assertions + */ + TestCluster.prototype.getResults = function (tests) { + var assertions, assertion; + tests = this.filterDataToTests(tests); + + if (tests.length === 1 || this.type === 'combined') { + assertions = getCombinedAssertions(this, tests); + + } else if (this.type === "stacking") { + assertions = getStackedAssertions(this, tests); + + } else if (window) { + window.console.error( + "Unknown type for cluster " + this.id + ); + } + + // Return a default assert if none was defined + if (assertions) { + if (assertions.length === 0) { + assertion = new quail.lib.EarlAssertion({ + testCase: this.id, + outcome: { + result: 'inapplicable' + } + }), + assertions.push(assertion); + } + return assertions; + } + }; + + return TestCluster; +}()); \ No newline at end of file diff --git a/src/resources/preconditions.yml b/src/resources/preconditions.yml new file mode 100644 index 000000000..86fe262b5 --- /dev/null +++ b/src/resources/preconditions.yml @@ -0,0 +1,56 @@ +videoMayBePresent: + type: "precondition" + testability: 1 + title: + en: "Video or object uses a link that points to a file with a video extension" + nl: "Video of object met een link naar een bestand met een video extensie" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "link" + - "video" + callback: "videoMayBePresent" +audioMayBePresent: + type: "precondition" + testability: 1 + title: + en: "Audio or object uses a link that points to a file with a video extension" + nl: "Audio of object met een link naar een bestand met een video extensie" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "link" + - "audio" + callback: "audioMayBePresent" +animatedGifMayBePresent: + type: "precondition" + testability: 1 + title: + en: "Test if a .gif is used on the page. Test if the .gif contains more then one frame" + nl: "Test of een .gif afbeelding gebruikt is op de pagina. Test of het .gif bestand uit meer dan één frame bestaat" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "link" + - "gif" + callback: "animatedGifMayBePresent" +userInputMayBeRequired: + type: "precondition" + testability: 1 + title: + en: "Test if user input is required" + nl: "Test of er invoervelden zijn" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "form" + - "input" + callback: "userInputMayBeRequired" \ No newline at end of file diff --git a/src/resources/wcag2.yml b/src/resources/wcag2.yml index b70a4707b..9f8140364 100644 --- a/src/resources/wcag2.yml +++ b/src/resources/wcag2.yml @@ -30,6 +30,10 @@ 1.2.1: id: "wcag20:media-equiv-av-only-alt" level: "wcag20:level_a" + preconditions: + - "videoMayBePresent" + - "audioMayBePresent" + - "animatedGifMayBePresent" 1.2.2: id: "wcag20:media-equiv-captions" level: "wcag20:level_a" @@ -41,12 +45,18 @@ 1.2.3: id: "wcag20:media-equiv-audio-desc" level: "wcag20:level_a" + preconditions: + - "videoMayBePresent" 1.2.4: id: "wcag20:media-equiv-real-time-captions" level: "wcag20:level_aa" + preconditions: + - "videoMayBePresent" 1.2.5: id: "wcag20:media-equiv-audio-desc-only" level: "wcag20:level_aa" + preconditions: + - "videoMayBePresent" 1.2.6: id: "wcag20:media-equiv-sign" level: "wcag20:level_aaa" @@ -144,6 +154,9 @@ 1.4.2: id: "wcag20:visual-audio-contrast-dis-audio" level: "wcag20:level_a" + preconditions: + - "videoMayBePresent" + - "audioMayBePresent" 1.4.3: id: "wcag20:visual-audio-contrast-contrast" level: "wcag20:level_aa" @@ -284,15 +297,21 @@ 3.3.1: id: "wcag20:minimize-error-identified" level: "wcag20:level_a" + preconditions: + - "userInputMayBeRequired" 3.3.2: id: "wcag20:minimize-error-cues" level: "wcag20:level_a" 3.3.3: id: "wcag20:minimize-error-suggestions" level: "wcag20:level_aa" + preconditions: + - "userInputMayBeRequired" 3.3.4: id: "wcag20:minimize-error-reversible" level: "wcag20:level_aa" + preconditions: + - "userInputMayBeRequired" 3.3.5: id: "wcag20:minimize-error-context-help" level: "wcag20:level_aaa" From dec6527ffec27266ae746630002d373c5e6bb8ad Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Wed, 15 Oct 2014 15:19:16 +0200 Subject: [PATCH 24/50] wcag2.js refactored to allow launch from options --- src/js/lib/wcag2.js | 82 ++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/src/js/lib/wcag2.js b/src/js/lib/wcag2.js index 4e6a63402..2b127dd0b 100644 --- a/src/js/lib/wcag2.js +++ b/src/js/lib/wcag2.js @@ -5,50 +5,64 @@ quail.lib.wcag2 = (function () { /** * Run Quail using WCAG 2 + * + * Options can be used either to tell Quail where to load the wcag2 structure file + * or to give it directly (if it's already loaded). For the first, jsonPath + * must be provided. For the second the wcag2.json data must be given to + * options.wcag2Structure and the tests data to options.accessibilityTests. + * * @param {[object]} options Quail options */ function runWCAG20Quail(options) { + if (options.wcag2Structure && options.accessibilityTests) { + startWCAG20Quail(options, options.wcag2Structure, options.accessibilityTests); - // Load the required json files - $.when( - $.ajax(options.jsonPath + '/wcag2.json', ajaxOpt), - $.ajax(options.jsonPath + '/tests.json', ajaxOpt)) + } else { + // Load the required json files + $.when( + $.ajax(options.jsonPath + '/wcag2.json', ajaxOpt), + $.ajax(options.jsonPath + '/tests.json', ajaxOpt)) - // Setup quail given the tests described in the json files - .done(function (wcag2Call, testsCall) { - var criteria, accessibilityTests, knownTests; - var allTests = []; - - criteria = $.map(wcag2Call[0], function (critData) { - return new quail.lib.wcag2.Criterion( - critData, testsCall[0]); + // Setup quail given the tests described in the json files + .done(function (wcag2Call, testsCall) { + startWCAG20Quail(options, wcag2Call, testsCall); }); + } + } - // Create the accessibiliyTests object, based on the - // tests in the criteria - $.each(criteria, function (i, criterion) { - allTests.push.apply(allTests, criterion.getTests()); - }); + function startWCAG20Quail(options, wcag2Call, testsCall) { + var criteria, accessibilityTests, knownTests; + var allTests = []; - knownTests = []; - accessibilityTests = []; + criteria = $.map(wcag2Call[0], function (critData) { + return new quail.lib.wcag2.Criterion( + critData, testsCall[0]); + }); - // Remove duplicates - // TODO: Figure out why some tests are created multiple times - $.each(allTests, function (i, test) { - if (knownTests.indexOf(test.title.en) === -1) { - knownTests.push(test.title.en); - accessibilityTests.push(test); - } - }); + // Create the accessibiliyTests object, based on the + // tests in the criteria + $.each(criteria, function (i, criterion) { + allTests.push.apply(allTests, criterion.getTests()); + }); - // Run quail with the tests instead of the guideline - $(quail.html).quail({ - accessibilityTests: accessibilityTests, - // Have wcag2 intercept the callback - testCollectionComplete: createCallback( - criteria, options.testCollectionComplete) - }); + knownTests = []; + accessibilityTests = []; + + // Remove duplicates + // TODO: Figure out why some tests are created multiple times + $.each(allTests, function (i, test) { + if (knownTests.indexOf(test.title.en) === -1) { + knownTests.push(test.title.en); + accessibilityTests.push(test); + } + }); + + // Run quail with the tests instead of the guideline + $(quail.html).quail({ + accessibilityTests: accessibilityTests, + // Have wcag2 intercept the callback + testCollectionComplete: createCallback( + criteria, options.testCollectionComplete) }); } From 83718c7627162728b039a94f5d295a8def4b937b Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Wed, 15 Oct 2014 16:27:32 +0200 Subject: [PATCH 25/50] - Added videoMayBePresent tests --- src/js/custom/audioMayBePresent.js | 22 +++++++ src/js/custom/videoMayBePresent.js | 66 +++++++++++++++++++ src/resources/preconditions.yml | 8 +-- src/resources/tests.yml | 57 +++++++++++++++- .../audioMayBePresent.html | 29 ++++++++ .../videoMayBePresent.html | 36 ++++++++++ 6 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 src/js/custom/audioMayBePresent.js create mode 100644 src/js/custom/videoMayBePresent.js create mode 100644 test/accessibility-tests/audioMayBePresent.html create mode 100644 test/accessibility-tests/videoMayBePresent.html diff --git a/src/js/custom/audioMayBePresent.js b/src/js/custom/audioMayBePresent.js new file mode 100644 index 000000000..168ffff2a --- /dev/null +++ b/src/js/custom/audioMayBePresent.js @@ -0,0 +1,22 @@ +quail.videoMayBePresent=function(quail, test, Case){ + + var testableElements = test.get('$scope').find('video object iframe'); + + if(testableElements.length === 0){ + var _case=Case({ + element: this, + status: 'inapplicable', + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + } + + test.get('$scope').find('video object').each(function(){ + var _case=Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + + test.add(_case); + }); +}; diff --git a/src/js/custom/videoMayBePresent.js b/src/js/custom/videoMayBePresent.js new file mode 100644 index 000000000..ee917795a --- /dev/null +++ b/src/js/custom/videoMayBePresent.js @@ -0,0 +1,66 @@ +quail.videoMayBePresent=function(quail, test, Case){ + + function getSource(element){ + var source=false; + + if (element.attr('src') !== undefined) { + return element.attr('src'); + } + + if (element.find('source').attr('src') !== undefined) { + return element.find('source').attr('src'); + } + + return source; + } + + test.get('$scope').each(function(){ + var testableElements=$(this).find('video, object, iframe, a'); + + if (testableElements.length === 0) { + var _case=Case({ + element: this, + status: 'inapplicable', + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + } + + testableElements.each(function(){ + var fileTypes=['webm', 'flv', 'ogv', 'ogg', 'avi', 'mov', 'qt', 'wmv', 'asf', 'mp4', 'm4p', 'm4v', 'mpg', 'mp2', 'mpeg', 'mpg', 'mpe', 'mpv', 'm2v', '3gp', '3g2']; + var _case=Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + var source = getSource($(this)); + if (source && $.inArray(source.split('.').pop(), fileTypes)) { + _case.set({ + 'status': 'cantTell' + }); + return; + }else + + if($(this).find('param').attr('value') !== undefined){ + source = $(this).find('param[name="flashvars"]').attr('value'); + $.each(fileTypes, function(index, item){ + if(source.indexOf("." + item) > -1) { + _case.set({ + 'status': 'cantTell' + }); + return false; + } + }); + return; + } + window.console.log("TEST3"); + + _case.set({ + 'status': 'inapplicable' + }); + + }); + + }); +}; diff --git a/src/resources/preconditions.yml b/src/resources/preconditions.yml index 86fe262b5..5a6369341 100644 --- a/src/resources/preconditions.yml +++ b/src/resources/preconditions.yml @@ -1,5 +1,5 @@ videoMayBePresent: - type: "precondition" + type: "custom" testability: 1 title: en: "Video or object uses a link that points to a file with a video extension" @@ -13,7 +13,7 @@ videoMayBePresent: - "video" callback: "videoMayBePresent" audioMayBePresent: - type: "precondition" + type: "custom" testability: 1 title: en: "Audio or object uses a link that points to a file with a video extension" @@ -27,7 +27,7 @@ audioMayBePresent: - "audio" callback: "audioMayBePresent" animatedGifMayBePresent: - type: "precondition" + type: "custom" testability: 1 title: en: "Test if a .gif is used on the page. Test if the .gif contains more then one frame" @@ -41,7 +41,7 @@ animatedGifMayBePresent: - "gif" callback: "animatedGifMayBePresent" userInputMayBeRequired: - type: "precondition" + type: "custom" testability: 1 title: en: "Test if user input is required" diff --git a/src/resources/tests.yml b/src/resources/tests.yml index 4c4dc7770..5b115c0c5 100644 --- a/src/resources/tests.yml +++ b/src/resources/tests.yml @@ -4993,4 +4993,59 @@ KINGUseCurrencyAsSymbol: tags: - "KING" callback: "KINGUseCurrencyAsSymbol" - +videoMayBePresent: + type: "custom" + testability: 1 + title: + en: "Video or object uses a link that points to a file with a video extension" + nl: "Video of object met een link naar een bestand met een video extensie" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "link" + - "video" + callback: "videoMayBePresent" +audioMayBePresent: + type: "custom" + testability: 1 + title: + en: "Audio or object uses a link that points to a file with a video extension" + nl: "Audio of object met een link naar een bestand met een video extensie" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "link" + - "audio" + callback: "audioMayBePresent" +animatedGifMayBePresent: + type: "custom" + testability: 1 + title: + en: "Test if a .gif is used on the page. Test if the .gif contains more then one frame" + nl: "Test of een .gif afbeelding gebruikt is op de pagina. Test of het .gif bestand uit meer dan één frame bestaat" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "link" + - "gif" + callback: "animatedGifMayBePresent" +userInputMayBeRequired: + type: "custom" + testability: 1 + title: + en: "Test if user input is required" + nl: "Test of er invoervelden zijn" + description: + en: "" + nl: "" + guidelines: [] + tags: + - "form" + - "input" + callback: "userInputMayBeRequired" diff --git a/test/accessibility-tests/audioMayBePresent.html b/test/accessibility-tests/audioMayBePresent.html new file mode 100644 index 000000000..d7106d36c --- /dev/null +++ b/test/accessibility-tests/audioMayBePresent.html @@ -0,0 +1,29 @@ + + + + audioMayBePresent + + +
+ +
+ +
+ +
+
+ + + + + + + +
+ + + + \ No newline at end of file diff --git a/test/accessibility-tests/videoMayBePresent.html b/test/accessibility-tests/videoMayBePresent.html new file mode 100644 index 000000000..c05403e6f --- /dev/null +++ b/test/accessibility-tests/videoMayBePresent.html @@ -0,0 +1,36 @@ + + + + videoMayBePresent + + +
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ + + + + +
+ + + + \ No newline at end of file From ad24a7a473775adda27160829e3625b817223f46 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Wed, 15 Oct 2014 17:03:21 +0200 Subject: [PATCH 26/50] Bugfix importing wcag2 option data --- src/js/lib/wcag2.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/lib/wcag2.js b/src/js/lib/wcag2.js index 2b127dd0b..bc4767416 100644 --- a/src/js/lib/wcag2.js +++ b/src/js/lib/wcag2.js @@ -25,7 +25,7 @@ quail.lib.wcag2 = (function () { // Setup quail given the tests described in the json files .done(function (wcag2Call, testsCall) { - startWCAG20Quail(options, wcag2Call, testsCall); + startWCAG20Quail(options, wcag2Call[0], testsCall[0]); }); } } @@ -34,9 +34,9 @@ quail.lib.wcag2 = (function () { var criteria, accessibilityTests, knownTests; var allTests = []; - criteria = $.map(wcag2Call[0], function (critData) { + criteria = $.map(wcag2Call, function (critData) { return new quail.lib.wcag2.Criterion( - critData, testsCall[0]); + critData, testsCall); }); // Create the accessibiliyTests object, based on the From b794ad3d2068433d63a4799f812aa52868053391 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Thu, 16 Oct 2014 09:54:56 +0200 Subject: [PATCH 27/50] - Improved tests - Added test for iframed video sources --- src/js/custom/videoMayBePresent.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/js/custom/videoMayBePresent.js b/src/js/custom/videoMayBePresent.js index ee917795a..df9994549 100644 --- a/src/js/custom/videoMayBePresent.js +++ b/src/js/custom/videoMayBePresent.js @@ -35,14 +35,14 @@ quail.videoMayBePresent=function(quail, test, Case){ test.add(_case); var source = getSource($(this)); - if (source && $.inArray(source.split('.').pop(), fileTypes)) { + if (source && $.inArray(source.split('.').pop(), fileTypes) > -1) { _case.set({ 'status': 'cantTell' }); return; - }else + } + if($(this).find('param[name="flashvars"]').attr('value') !== undefined){ - if($(this).find('param').attr('value') !== undefined){ source = $(this).find('param[name="flashvars"]').attr('value'); $.each(fileTypes, function(index, item){ if(source.indexOf("." + item) > -1) { @@ -54,13 +54,12 @@ quail.videoMayBePresent=function(quail, test, Case){ }); return; } - window.console.log("TEST3"); - - _case.set({ - 'status': 'inapplicable' - }); - + if(source.indexOf('//www.youtube.com/embed/') > -1 || source.indexOf('//player.vimeo.com/video/') > -1){ + _case.set({ + 'status': 'cantTell' + }); + return; + } }); - }); }; From 3348c24c980245b103d50979c8620ed2346f98fd Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Thu, 16 Oct 2014 10:16:52 +0200 Subject: [PATCH 28/50] - Added audioMayBePresent tests --- src/js/custom/audioMayBePresent.js | 67 ++++++++++++++----- .../audioMayBePresent.html | 2 +- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/js/custom/audioMayBePresent.js b/src/js/custom/audioMayBePresent.js index 168ffff2a..7d08e4bd7 100644 --- a/src/js/custom/audioMayBePresent.js +++ b/src/js/custom/audioMayBePresent.js @@ -1,22 +1,59 @@ -quail.videoMayBePresent=function(quail, test, Case){ +quail.audioMayBePresent=function(quail, test, Case){ - var testableElements = test.get('$scope').find('video object iframe'); + function getSource(element){ + var source=false; - if(testableElements.length === 0){ - var _case=Case({ - element: this, - status: 'inapplicable', - expected: $(this).closest('.quail-test').data('expected') - }); - test.add(_case); + if (element.attr('src') !== undefined) { + return element.attr('src'); + } + + if (element.find('source').attr('src') !== undefined) { + return element.find('source').attr('src'); + } + + return source; } - test.get('$scope').find('video object').each(function(){ - var _case=Case({ - element: this, - expected: $(this).closest('.quail-test').data('expected') - }); + test.get('$scope').each(function(){ + var testableElements=$(this).find('audio, object'); - test.add(_case); + if (testableElements.length === 0) { + var _case=Case({ + element: this, + status: 'inapplicable', + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + } + + testableElements.each(function(){ + var fileTypes=['webm', 'flv', 'ogv', 'ogg', 'avi', 'mov', 'qt', 'wmv', 'asf', 'mp4', 'm4p', 'm4v', 'mpg', 'mp2', 'mpeg', 'mpg', 'mpe', 'mpv', 'm2v', '3gp', '3g2']; + var _case=Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + var source = getSource($(this)); + if (source && $.inArray(source.split('.').pop(), fileTypes) > -1) { + _case.set({ + 'status': 'cantTell' + }); + return; + } + if($(this).find('param[name="src"]').attr('value') !== undefined){ + + source = $(this).find('param[name="src"]').attr('value'); + $.each(fileTypes, function(index, item){ + if(source.indexOf("." + item) > -1) { + _case.set({ + 'status': 'cantTell' + }); + return false; + } + }); + return; + } + }); }); }; diff --git a/test/accessibility-tests/audioMayBePresent.html b/test/accessibility-tests/audioMayBePresent.html index d7106d36c..453158791 100644 --- a/test/accessibility-tests/audioMayBePresent.html +++ b/test/accessibility-tests/audioMayBePresent.html @@ -5,7 +5,7 @@
- +
From 9271d48b22fb27cdd0802b67a6365e9ee0c60da5 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Thu, 16 Oct 2014 11:18:26 +0200 Subject: [PATCH 29/50] core- check for guideline=wcag2 before accessibilityTests --- src/js/core.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js/core.js b/src/js/core.js index 3ad4a2aae..a66c23816 100644 --- a/src/js/core.js +++ b/src/js/core.js @@ -209,18 +209,18 @@ var quail = { }); _run.call(quail); } - // If a list of specific tests is provided, use them. - else if (options.accessibilityTests) { - buildTests(quail, options.accessibilityTests, options); - _run.call(quail); - } - // Let wcag2 run itself, will call quail again when it knows what // to else if (options.guideline === 'wcag2') { quail.lib.wcag2.run(options); } + // If a list of specific tests is provided, use them. + else if (options.accessibilityTests) { + buildTests(quail, options.accessibilityTests, options); + _run.call(quail); + } + // Otherwise get the tests from the json data list. else { var url = options.jsonPath; From e14dabb05558d2ab0fa38a0f466f1e465a498782 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Thu, 16 Oct 2014 12:08:10 +0200 Subject: [PATCH 30/50] - Added animatedGifMayBePresent tests --- src/js/custom/animatedGifMayBePresent.js | 92 +++++++++++++++++++ .../animatedGifMayBePresent.html | 22 +++++ 2 files changed, 114 insertions(+) create mode 100644 src/js/custom/animatedGifMayBePresent.js create mode 100644 test/accessibility-tests/animatedGifMayBePresent.html diff --git a/src/js/custom/animatedGifMayBePresent.js b/src/js/custom/animatedGifMayBePresent.js new file mode 100644 index 000000000..6b6219471 --- /dev/null +++ b/src/js/custom/animatedGifMayBePresent.js @@ -0,0 +1,92 @@ +quail.animatedGifMayBePresent=function(quail, test, Case){ + + /** + * Test if gif is animated + * Implemented from: https://gist.github.com/3012623.git + * @param src + * @param ext + * @param cb + */ + function isAnimatedGif(src, ext, cb){ + + if(ext !== 'gif'){ + cb(false); + return; + } + + var request=new XMLHttpRequest(); + request.open('GET', src, true); + request.responseType='arraybuffer'; + request.addEventListener('load', function () { + var arr = new Uint8Array(request.response); + var frames = 0; + + // make sure it's a gif (GIF8) + if (arr[0] !== 0x47 || arr[1] !== 0x49 || + arr[2] !== 0x46 || arr[3] !== 0x38) + { + cb(false); + return; + } + + //ported from php http://www.php.net/manual/en/function.imagecreatefromgif.php#104473 + //an animated gif contains multiple "frames", with each frame having a + //header made up of: + // * a static 4-byte sequence (\x00\x21\xF9\x04) + // * 4 variable bytes + // * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?) + // We read through the file til we reach the end of the file, or we've found + // at least 2 frame headers + for (var i=0; i < arr.length -9; i++) { + if (arr[i] === 0x00 && arr[i+1] === 0x21 && + arr[i+2] === 0xF9 && arr[i+3] === 0x04 && + arr[i+8] === 0x00 && + (arr[i+9] === 0x2C || arr[i+9] === 0x21)) + { + frames++; + } + if(frames > 1){ + cb(true); + return; + } + } + + cb(false); + window.console.log("AFTER PARSING: ", frames); + }); + request.send(); + } + + test.get('$scope').find('img').each(function(){ + + var _case=Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + var imgSrc=$(this).attr('src'); + var ext=$(this).attr('src').split('.').pop().toLowerCase(); + + if (ext !== 'gif') { + _case.set({ + 'status': 'inapplicable' + }); + return; + } + + isAnimatedGif(imgSrc, ext, function(animated){ + if (animated) { + _case.set({ + 'status': 'cantTell' + }); + return; + } else{ + _case.set({ + 'status': 'inapplicable' + }); + return; + } + }); + }); +}; diff --git a/test/accessibility-tests/animatedGifMayBePresent.html b/test/accessibility-tests/animatedGifMayBePresent.html new file mode 100644 index 000000000..7b968c3df --- /dev/null +++ b/test/accessibility-tests/animatedGifMayBePresent.html @@ -0,0 +1,22 @@ + + + + animatedGifMayBePresent + + +
+ eat at Joes +
+ +
+ + eat at Joes +
+ +
+ A brown and black cat named Rex. +
+ + + + \ No newline at end of file From d8561b222df26165dd2da59ef9e95a2b1cb409d1 Mon Sep 17 00:00:00 2001 From: Marco Alting Date: Thu, 16 Oct 2014 12:53:51 +0200 Subject: [PATCH 31/50] - Added tests forn userInputMayBeRequired --- src/js/custom/userInputMayBeRequired.js | 40 +++++++++++ .../userInputMayBeRequired.html | 68 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/js/custom/userInputMayBeRequired.js create mode 100644 test/accessibility-tests/userInputMayBeRequired.html diff --git a/src/js/custom/userInputMayBeRequired.js b/src/js/custom/userInputMayBeRequired.js new file mode 100644 index 000000000..820e7f090 --- /dev/null +++ b/src/js/custom/userInputMayBeRequired.js @@ -0,0 +1,40 @@ +quail.userInputMayBeRequired=function(quail, test, Case){ + test.get('$scope').each(function(){ + + var _case=Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + var forms = $(this).find('form'); + var formInputs = 0; + var inputsOutsideForm = $(this).find('input:not(form input, [type=button],[type=reset],[type=image],[type=submit],[type=hidden])'); + + forms.each(function(){ + var inputs=$(this).find('input:not([type=button],[type=reset],[type=image],[type=submit],[type=hidden])'); + if (inputs.length > 1) { + formInputs = inputs.length; + } + }); + + if(formInputs > 0){ + _case.set({ + 'status': 'cantTell' + }); + return; + } + + if(inputsOutsideForm.length > 1) { + _case.set({ + 'status': 'cantTell' + }); + return; + } + + _case.set({ + 'status': 'inapplicable' + }); + + }); +}; diff --git a/test/accessibility-tests/userInputMayBeRequired.html b/test/accessibility-tests/userInputMayBeRequired.html new file mode 100644 index 000000000..8361eacab --- /dev/null +++ b/test/accessibility-tests/userInputMayBeRequired.html @@ -0,0 +1,68 @@ + + + + userInputMayBeRequired + + +
+ +
+
+ +
+
+
+ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+ +
+
+
+ + +
+
+
+ + +
+
+ + + + \ No newline at end of file From 79863baef9559d7fe094d7e489f52cdae23793b4 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Thu, 23 Oct 2014 11:08:20 +0200 Subject: [PATCH 32/50] Refactored wcag2 object for preconditions --- Gruntfile.js | 4 +- src/js/lib/earlAssertion.js | 51 ------ src/js/lib/wcag/criterion.js | 69 -------- src/js/lib/wcag/testClusters.js | 260 ------------------------------ src/js/lib/wcag2.js | 8 +- src/js/lib/wcag2/criterion.js | 4 +- src/js/lib/wcag2/earlAssertion.js | 2 +- src/js/lib/wcag2/testClusters.js | 17 +- 8 files changed, 18 insertions(+), 397 deletions(-) delete mode 100644 src/js/lib/earlAssertion.js delete mode 100644 src/js/lib/wcag/criterion.js delete mode 100644 src/js/lib/wcag/testClusters.js diff --git a/Gruntfile.js b/Gruntfile.js index 295b3fcf7..27bfcf6bb 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -32,11 +32,11 @@ module.exports = function(grunt) { stripBanners: true }, dist: { - src: ['src/js/core.js', 'src/js/components/*.js', 'src/js/strings/*.js', 'src/js/custom/*.js', 'src/js/lib/*.js', 'src/js/lib/wcag/*.js'], + src: ['src/js/core.js', 'src/js/components/*.js', 'src/js/strings/*.js', 'src/js/custom/*.js', 'src/js/lib/*.js', 'src/js/lib/wcag/*.js', 'src/js/lib/wcag2/*.js'], dest: 'dist/quail.jquery.js' }, test: { - src: ['src/js/core.js', 'src/js/components/*.js', 'src/js/strings/*.js', 'src/js/custom/*.js', 'src/js/lib/*.js', 'src/js/lib/wcag/*.js'], + src: ['src/js/core.js', 'src/js/components/*.js', 'src/js/strings/*.js', 'src/js/custom/*.js', 'src/js/lib/*.js', 'src/js/lib/wcag/*.js', 'src/js/lib/wcag2/*.js'], dest: 'test/quail-testing.jquery.js', options: { banner: '<%= pkg.options.banner %>' + "\n" + 'var __testQuail = {};(function($) {' + "\n", diff --git a/src/js/lib/earlAssertion.js b/src/js/lib/earlAssertion.js deleted file mode 100644 index 2b902fe40..000000000 --- a/src/js/lib/earlAssertion.js +++ /dev/null @@ -1,51 +0,0 @@ -quail.lib.EarlAssertion = (function () { - var pageUrl; - var resultPriorityMap = [ - 'untested', 'inapplicable', 'passed', - 'cantTell', 'failed' - ]; - var defaultAssertion = { - type: 'assertion', - subject: pageUrl, - assertedBy: { - type: 'earl:Software', - name: 'QuailJS' - }, - mode: 'automated' - }; - - - if (window && window.location) { - pageUrl = window.location.href; - } - - /** - * Create a new earl assert object - * @param {object} base Properties from this object are added to the Assertion - * and override the default. - */ - function Assertion(base) { - $.extend(this, base, defaultAssertion); - this.outcome = $.extend({}, this.outcome); - } - - - /** - * Return the priorty index of the result - * @param {result|assert|outcome} val - * @return {integer} Result index in order of prioerty - */ - Assertion.getResultPriority = function (val) { - if (typeof val === 'object') { - if (val.outcome) { - val = val.outcome.result; - } else { - val = val.result; - } - } - return resultPriorityMap.indexOf(val); - }; - - return Assertion; - -}()); \ No newline at end of file diff --git a/src/js/lib/wcag/criterion.js b/src/js/lib/wcag/criterion.js deleted file mode 100644 index 2a498ebd4..000000000 --- a/src/js/lib/wcag/criterion.js +++ /dev/null @@ -1,69 +0,0 @@ -quail.lib.wcag2.Criterion = (function () { - - // Provide default values for the assert objects - function aggregateParts(parts, defaultResult) { - var getResultPriority = quail.lib.EarlAssertion.getResultPriority; - var outcome = {result: defaultResult}; - - $.each(parts, function (i, part) { - if (getResultPriority(outcome) < getResultPriority(part)) { - outcome.result = part.outcome.result; - } - }); - return outcome; - } - - - function constructor (data, testDefinitions) { - var testClusters = []; - var criterion = {}; - var defaultResult = data['default'] || 'untested'; - var id = data.id; - - // Create a testCluster object for each cluster (if any) - if ($.isArray(data.testClusters)) { - testClusters = $.map(data.testClusters, function (clusterConf) { - return new quail.lib.wcag2.TestCluster( - clusterConf, testDefinitions - ); - }); - } - - - criterion.getResult = function (data) { - var result; - var parts = []; - - $.each(testClusters, function (i, cluster) { - var part = cluster.getResults(data); - parts.push.apply(parts, part); - }); - result = new quail.lib.EarlAssertion({ - testRequirement: id, - outcome: aggregateParts(parts, defaultResult) - }); - if (parts.length > 0) { - result.hasPart = parts; - } - return result; - }; - - /** - * Get an array of test used in this criterion - * @param {[json]} criteria - * @return {[array]} - */ - criterion.getTests = function () { - var tests = []; - $.each(testClusters, function (i, cluster) { - tests.push.apply(tests, cluster.tests); - }); - return tests; - }; - - return criterion; - } - - return constructor; - -}()); diff --git a/src/js/lib/wcag/testClusters.js b/src/js/lib/wcag/testClusters.js deleted file mode 100644 index 51aa4f953..000000000 --- a/src/js/lib/wcag/testClusters.js +++ /dev/null @@ -1,260 +0,0 @@ -quail.lib.wcag2.TestCluster = (function () { - - var pointerMap = { - elms: [], - pointers: [], - add: function (testCase) { - var pointer; - if (pointerMap.elms.indexOf(testCase.get('element')) === -1) { - if (testCase.get('html')) { - pointer = [{ - type: 'CharSnippetCompoundPointer', - chars: testCase.get('html'), - CSSSelector: testCase.get('selector') - }]; - } - pointerMap.elms.push(testCase.get('element')); - pointerMap.pointers.push(pointer); - } - }, - getPointer: function (elm) { - var index = pointerMap.elms.indexOf(elm); - return pointerMap.pointers[index]; - } - }; - - /** - * Run the callback for each testcase within the array of tests - * @param {array} tests - * @param {Function} callback Given the parameters (test, testcase) - */ - function eachTestCase(tests, callback) { - $.each(tests, function (i, test) { - test.each(function () { - callback.call(this, test, this); - }); - }); - } - - /** - * Get an array of elements common to all tests provided - * @param {Object} tests - * @return {Array} Array of HTML elements - */ - function getCommonElements(tests) { - var common = []; - var map = []; - - $.each(tests, function (i, test) { - var elms = []; - test.each(function () { - elms.push(this.get('element')); - pointerMap.add(this); - }); - map.push(elms); - }); - $.each(map, function (i, arr) { - if (i === 0) { - common = arr; - return; - } - var newArr = []; - $.each(arr, function (i, val) { - if (common.indexOf(val) !== -1) { - newArr.push(val); - } - }); - common = newArr; - }); - - return common; - } - - /** - * Get an array of elements in the given tests - * @param {Object} tests - * @return {Array} Array of HTML elements - */ - function getAllElements(tests) { - var elms = []; - eachTestCase(tests, function (test, testCase) { - var elm = testCase.get('element'); - if (elms.indexOf(elm) === -1) { - elms.push(elm); - pointerMap.add(testCase); - } - }); - return elms; - } - - /** - * Look at each unique element and create an assert for it - * @param {array[DOM element]} elms - * @param {object} base Base object for the assert - * @return {array[assert]} Array with asserts - */ - function createAssertionsForEachElement(elms, base) { - var assertions = []; - // Create asserts for each element - $.each(elms, function (i, elm) { - var assertion = new quail.lib.EarlAssertion(base); - if (elm) { // Don't do undefined pointers - assertion.outcome.pointer = pointerMap.getPointer(elm); - } - assertions.push(assertion); - }); - return assertions; - } - - /** - * Combine the test results of a cluster into asserts - * @param {Object} cluster - * @param {Array[Object]} tests - * @return {Array[Object]} Array of Asserts - */ - function getCombinedAssertions(cluster, tests) { - var elms = getCommonElements(tests); - var assertions = createAssertionsForEachElement(elms, { - testCase: cluster.id, - outcome: {result: 'failed'} - }); - - // Iterate over all results to build the assert - eachTestCase(tests, function (test, testCase) { - // Look up the assert, if any - var newResult = testCase.get('status'); - var getResultPriority = quail.lib.EarlAssertion.getResultPriority; - var assertion = assertions[elms.indexOf( - testCase.get('element') - )]; - - // Allow the cluster to override the results - if (cluster[newResult]) { - newResult = cluster[newResult]; - } - - // Override if the resultId is higher or equal (combine) - if (assertion && getResultPriority(assertion) >= getResultPriority(newResult)) { - var pointer = assertion.outcome.pointer; - assertion.outcome = { - result: newResult, - info: test.get('title') - }; - if (pointer) { - assertion.outcome.pointer = pointer; - } - } - }); - - return assertions; - } - - - /** - * Stack the test results of a cluster into asserts - * @param {Object} cluster - * @param {Array[Object]} tests - * @return {Array[Object]} Array of Asserts - */ - function getStackedAssertions(cluster, tests) { - var elms = getAllElements(tests); - var asserts = createAssertionsForEachElement(elms, { - testCase: cluster.id, - outcome: { result: 'untested'} - }); - - // Iterate over all results to build the assert - eachTestCase(tests, function (test, testCase) { - // Look up the assert, if any - var newResult = testCase.get('status'); - var getResultPriority = quail.lib.EarlAssertion.getResultPriority; - var assert = asserts[elms.indexOf( - testCase.get('element') - )]; - - // Allow the cluster to override the results - if (cluster[newResult]) { - newResult = cluster[newResult]; - } - - // Override if the resultId is lower (stacked) - if (assert && getResultPriority(assert) < getResultPriority(newResult)) { - assert.outcome = { - result: newResult, - info: test.get('title') - }; - } - }); - return asserts; - } - - - function TestCluster(config, testDefinitions) { - $.extend(this, { - id: config.tests.join('+') - }, config); - - this.tests = $.map(this.tests, function (test) { - return testDefinitions[test]; - }); - } - - - /** - * Filter the data array so it only contains results - * from this cluster - * @param {Array} data - * @return {Array} - */ - TestCluster.prototype.filterDataToTests = function (data) { - var names = $.map(this.tests, function (test) { - return test.name; - }); - var testData = []; - - $.each(data, function (i, result) { - if (names.indexOf(result.get('name')) !== -1) { - testData.push(result); - } - }); - return testData; - }; - - /** - * Get the results of this test cluster based on the tests provided for it - * @param {object} tests As provided by Quail - * @return {object} EARL assertions - */ - TestCluster.prototype.getResults = function (tests) { - var assertions, assertion; - tests = this.filterDataToTests(tests); - - if (tests.length === 1 || this.type === 'combined') { - assertions = getCombinedAssertions(this, tests); - - } else if (this.type === "stacking") { - assertions = getStackedAssertions(this, tests); - - } else if (window) { - window.console.error( - "Unknown type for cluster " + this.id - ); - } - - // Return a default assert if none was defined - if (assertions) { - if (assertions.length === 0) { - assertion = new quail.lib.EarlAssertion({ - testCase: this.id, - outcome: { - result: 'inapplicable' - } - }), - assertions.push(assertion); - } - return assertions; - } - }; - - return TestCluster; -}()); \ No newline at end of file diff --git a/src/js/lib/wcag2.js b/src/js/lib/wcag2.js index 8155894f5..5689c78dc 100644 --- a/src/js/lib/wcag2.js +++ b/src/js/lib/wcag2.js @@ -12,17 +12,17 @@ quail.lib.wcag2 = (function () { // Load the required json files $.when( $.ajax(options.jsonPath + '/wcag2.json', ajaxOpt), - $.ajax(options.jsonPath + '/preconditions.json', ajaxOpt), - $.ajax(options.jsonPath + '/tests.json', ajaxOpt)) + $.ajax(options.jsonPath + '/tests.json', ajaxOpt), + $.ajax(options.jsonPath + '/preconditions.json', ajaxOpt)) // Setup quail given the tests described in the json files - .done(function (wcag2Call, testsCall) { + .done(function (wcag2Call, testsCall, preconditionCall) { var criteria, accessibilityTests, knownTests; var allTests = []; criteria = $.map(wcag2Call[0], function (critData) { return new quail.lib.wcag2.Criterion( - critData, testsCall[0]); + critData, testsCall[0], preconditionCall[0]); }); // Create the accessibiliyTests object, based on the diff --git a/src/js/lib/wcag2/criterion.js b/src/js/lib/wcag2/criterion.js index 2a498ebd4..2c8fc72d4 100644 --- a/src/js/lib/wcag2/criterion.js +++ b/src/js/lib/wcag2/criterion.js @@ -2,7 +2,7 @@ quail.lib.wcag2.Criterion = (function () { // Provide default values for the assert objects function aggregateParts(parts, defaultResult) { - var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var getResultPriority = quail.lib.wcag2.EarlAssertion.getResultPriority; var outcome = {result: defaultResult}; $.each(parts, function (i, part) { @@ -38,7 +38,7 @@ quail.lib.wcag2.Criterion = (function () { var part = cluster.getResults(data); parts.push.apply(parts, part); }); - result = new quail.lib.EarlAssertion({ + result = new quail.lib.wcag2.EarlAssertion({ testRequirement: id, outcome: aggregateParts(parts, defaultResult) }); diff --git a/src/js/lib/wcag2/earlAssertion.js b/src/js/lib/wcag2/earlAssertion.js index 2b902fe40..28beefca4 100644 --- a/src/js/lib/wcag2/earlAssertion.js +++ b/src/js/lib/wcag2/earlAssertion.js @@ -1,4 +1,4 @@ -quail.lib.EarlAssertion = (function () { +quail.lib.wcag2.EarlAssertion = (function () { var pageUrl; var resultPriorityMap = [ 'untested', 'inapplicable', 'passed', diff --git a/src/js/lib/wcag2/testClusters.js b/src/js/lib/wcag2/testClusters.js index 51aa4f953..f221df7f2 100644 --- a/src/js/lib/wcag2/testClusters.js +++ b/src/js/lib/wcag2/testClusters.js @@ -97,7 +97,7 @@ quail.lib.wcag2.TestCluster = (function () { var assertions = []; // Create asserts for each element $.each(elms, function (i, elm) { - var assertion = new quail.lib.EarlAssertion(base); + var assertion = new quail.lib.wcag2.EarlAssertion(base); if (elm) { // Don't do undefined pointers assertion.outcome.pointer = pointerMap.getPointer(elm); } @@ -123,7 +123,7 @@ quail.lib.wcag2.TestCluster = (function () { eachTestCase(tests, function (test, testCase) { // Look up the assert, if any var newResult = testCase.get('status'); - var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var getResultPriority = quail.lib.wcag2.EarlAssertion.getResultPriority; var assertion = assertions[elms.indexOf( testCase.get('element') )]; @@ -167,7 +167,7 @@ quail.lib.wcag2.TestCluster = (function () { eachTestCase(tests, function (test, testCase) { // Look up the assert, if any var newResult = testCase.get('status'); - var getResultPriority = quail.lib.EarlAssertion.getResultPriority; + var getResultPriority = quail.lib.wcag2.EarlAssertion.getResultPriority; var assert = asserts[elms.indexOf( testCase.get('element') )]; @@ -227,15 +227,16 @@ quail.lib.wcag2.TestCluster = (function () { */ TestCluster.prototype.getResults = function (tests) { var assertions, assertion; - tests = this.filterDataToTests(tests); + var filteredTests = this.filterDataToTests(tests); - if (tests.length === 1 || this.type === 'combined') { - assertions = getCombinedAssertions(this, tests); + if (filteredTests.length === 1 || this.type === 'combined') { + assertions = getCombinedAssertions(this, filteredTests); } else if (this.type === "stacking") { - assertions = getStackedAssertions(this, tests); + assertions = getStackedAssertions(this, filteredTests); } else if (window) { + console.log(tests); window.console.error( "Unknown type for cluster " + this.id ); @@ -244,7 +245,7 @@ quail.lib.wcag2.TestCluster = (function () { // Return a default assert if none was defined if (assertions) { if (assertions.length === 0) { - assertion = new quail.lib.EarlAssertion({ + assertion = new quail.lib.wcag2.EarlAssertion({ testCase: this.id, outcome: { result: 'inapplicable' From 329b36a4e10dd4c18abaff06bf34ba8c3511111a Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Thu, 23 Oct 2014 11:41:47 +0200 Subject: [PATCH 33/50] Added precondition to Criterion --- src/js/lib/wcag2/criterion.js | 16 +++++++++++++++- src/js/lib/wcag2/testClusters.js | 7 ++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/js/lib/wcag2/criterion.js b/src/js/lib/wcag2/criterion.js index 2c8fc72d4..76607dcc5 100644 --- a/src/js/lib/wcag2/criterion.js +++ b/src/js/lib/wcag2/criterion.js @@ -14,7 +14,7 @@ quail.lib.wcag2.Criterion = (function () { } - function constructor (data, testDefinitions) { + function constructor (data, testDefinitions, preconditionDefinitions) { var testClusters = []; var criterion = {}; var defaultResult = data['default'] || 'untested'; @@ -29,6 +29,20 @@ quail.lib.wcag2.Criterion = (function () { }); } + // Create a precondition test + if ($.isArray(data.preconditions)) { + var preconditionTest = { + passed: 'inapplicable', + failed: 'cantTell', + type: 'stacking', // If any of it's content is found it should return cantTell + tests: data.preconditions + }; + // Add a test cluster for the precondition tests + testClusters.push(new quail.lib.wcag2.TestCluster( + preconditionTest, preconditionDefinitions + )); + } + criterion.getResult = function (data) { var result; diff --git a/src/js/lib/wcag2/testClusters.js b/src/js/lib/wcag2/testClusters.js index f221df7f2..0bbcd5640 100644 --- a/src/js/lib/wcag2/testClusters.js +++ b/src/js/lib/wcag2/testClusters.js @@ -108,6 +108,9 @@ quail.lib.wcag2.TestCluster = (function () { /** * Combine the test results of a cluster into asserts + * + * A combinbing cluster is a cluster which only fails if all it's tests fail + * * @param {Object} cluster * @param {Array[Object]} tests * @return {Array[Object]} Array of Asserts @@ -152,6 +155,9 @@ quail.lib.wcag2.TestCluster = (function () { /** * Stack the test results of a cluster into asserts + * + * A stacked cluster is one that fails if any of the tests fail + * * @param {Object} cluster * @param {Array[Object]} tests * @return {Array[Object]} Array of Asserts @@ -236,7 +242,6 @@ quail.lib.wcag2.TestCluster = (function () { assertions = getStackedAssertions(this, filteredTests); } else if (window) { - console.log(tests); window.console.error( "Unknown type for cluster " + this.id ); From 0591deb6a706d1d87fedea854b7dc1b0546c3f7f Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Thu, 23 Oct 2014 11:51:24 +0200 Subject: [PATCH 34/50] testCluster renamed to testAggregator --- src/js/lib/wcag2/criterion.js | 24 +++++----- .../{testClusters.js => testAggregator.js} | 48 +++++++++---------- src/resources/wcag2.yml | 20 ++++---- 3 files changed, 46 insertions(+), 46 deletions(-) rename src/js/lib/wcag2/{testClusters.js => testAggregator.js} (83%) diff --git a/src/js/lib/wcag2/criterion.js b/src/js/lib/wcag2/criterion.js index 76607dcc5..85fe95393 100644 --- a/src/js/lib/wcag2/criterion.js +++ b/src/js/lib/wcag2/criterion.js @@ -15,16 +15,16 @@ quail.lib.wcag2.Criterion = (function () { function constructor (data, testDefinitions, preconditionDefinitions) { - var testClusters = []; + var testAggregators = []; var criterion = {}; var defaultResult = data['default'] || 'untested'; var id = data.id; - // Create a testCluster object for each cluster (if any) - if ($.isArray(data.testClusters)) { - testClusters = $.map(data.testClusters, function (clusterConf) { - return new quail.lib.wcag2.TestCluster( - clusterConf, testDefinitions + // Create a TestAggregator object for each aggregator (if any) + if ($.isArray(data.testAggregators)) { + testAggregators = $.map(data.testAggregators, function (aggregateConf) { + return new quail.lib.wcag2.TestAggregator( + aggregateConf, testDefinitions ); }); } @@ -37,8 +37,8 @@ quail.lib.wcag2.Criterion = (function () { type: 'stacking', // If any of it's content is found it should return cantTell tests: data.preconditions }; - // Add a test cluster for the precondition tests - testClusters.push(new quail.lib.wcag2.TestCluster( + // Add a test aggregator for the precondition tests + testAggregators.push(new quail.lib.wcag2.TestAggregator( preconditionTest, preconditionDefinitions )); } @@ -48,8 +48,8 @@ quail.lib.wcag2.Criterion = (function () { var result; var parts = []; - $.each(testClusters, function (i, cluster) { - var part = cluster.getResults(data); + $.each(testAggregators, function (i, aggregator) { + var part = aggregator.getResults(data); parts.push.apply(parts, part); }); result = new quail.lib.wcag2.EarlAssertion({ @@ -69,8 +69,8 @@ quail.lib.wcag2.Criterion = (function () { */ criterion.getTests = function () { var tests = []; - $.each(testClusters, function (i, cluster) { - tests.push.apply(tests, cluster.tests); + $.each(testAggregators, function (i, aggregator) { + tests.push.apply(tests, aggregator.tests); }); return tests; }; diff --git a/src/js/lib/wcag2/testClusters.js b/src/js/lib/wcag2/testAggregator.js similarity index 83% rename from src/js/lib/wcag2/testClusters.js rename to src/js/lib/wcag2/testAggregator.js index 0bbcd5640..5bb40f574 100644 --- a/src/js/lib/wcag2/testClusters.js +++ b/src/js/lib/wcag2/testAggregator.js @@ -1,4 +1,4 @@ -quail.lib.wcag2.TestCluster = (function () { +quail.lib.wcag2.TestAggregator = (function () { var pointerMap = { elms: [], @@ -107,18 +107,18 @@ quail.lib.wcag2.TestCluster = (function () { } /** - * Combine the test results of a cluster into asserts + * Combine the test results of an Aggregator into asserts * - * A combinbing cluster is a cluster which only fails if all it's tests fail + * A combinbing aggregator is an aggregator which only fails if all it's tests fail * - * @param {Object} cluster + * @param {Object} aggregator * @param {Array[Object]} tests * @return {Array[Object]} Array of Asserts */ - function getCombinedAssertions(cluster, tests) { + function getCombinedAssertions(aggregator, tests) { var elms = getCommonElements(tests); var assertions = createAssertionsForEachElement(elms, { - testCase: cluster.id, + testCase: aggregator.id, outcome: {result: 'failed'} }); @@ -131,9 +131,9 @@ quail.lib.wcag2.TestCluster = (function () { testCase.get('element') )]; - // Allow the cluster to override the results - if (cluster[newResult]) { - newResult = cluster[newResult]; + // Allow the aggregator to override the results + if (aggregator[newResult]) { + newResult = aggregator[newResult]; } // Override if the resultId is higher or equal (combine) @@ -154,18 +154,18 @@ quail.lib.wcag2.TestCluster = (function () { /** - * Stack the test results of a cluster into asserts + * Stack the test results of a aggregator into asserts * - * A stacked cluster is one that fails if any of the tests fail + * A stacked aggregator is one that fails if any of the tests fail * - * @param {Object} cluster + * @param {Object} aggregator * @param {Array[Object]} tests * @return {Array[Object]} Array of Asserts */ - function getStackedAssertions(cluster, tests) { + function getStackedAssertions(aggregator, tests) { var elms = getAllElements(tests); var asserts = createAssertionsForEachElement(elms, { - testCase: cluster.id, + testCase: aggregator.id, outcome: { result: 'untested'} }); @@ -178,9 +178,9 @@ quail.lib.wcag2.TestCluster = (function () { testCase.get('element') )]; - // Allow the cluster to override the results - if (cluster[newResult]) { - newResult = cluster[newResult]; + // Allow the aggregator to override the results + if (aggregator[newResult]) { + newResult = aggregator[newResult]; } // Override if the resultId is lower (stacked) @@ -195,7 +195,7 @@ quail.lib.wcag2.TestCluster = (function () { } - function TestCluster(config, testDefinitions) { + function TestAggregator(config, testDefinitions) { $.extend(this, { id: config.tests.join('+') }, config); @@ -208,11 +208,11 @@ quail.lib.wcag2.TestCluster = (function () { /** * Filter the data array so it only contains results - * from this cluster + * from this aggregator * @param {Array} data * @return {Array} */ - TestCluster.prototype.filterDataToTests = function (data) { + TestAggregator.prototype.filterDataToTests = function (data) { var names = $.map(this.tests, function (test) { return test.name; }); @@ -227,11 +227,11 @@ quail.lib.wcag2.TestCluster = (function () { }; /** - * Get the results of this test cluster based on the tests provided for it + * Get the results of this test aggregator based on the tests provided for it * @param {object} tests As provided by Quail * @return {object} EARL assertions */ - TestCluster.prototype.getResults = function (tests) { + TestAggregator.prototype.getResults = function (tests) { var assertions, assertion; var filteredTests = this.filterDataToTests(tests); @@ -243,7 +243,7 @@ quail.lib.wcag2.TestCluster = (function () { } else if (window) { window.console.error( - "Unknown type for cluster " + this.id + "Unknown type for aggregator " + this.id ); } @@ -262,5 +262,5 @@ quail.lib.wcag2.TestCluster = (function () { } }; - return TestCluster; + return TestAggregator; }()); \ No newline at end of file diff --git a/src/resources/wcag2.yml b/src/resources/wcag2.yml index 9f8140364..423060a10 100644 --- a/src/resources/wcag2.yml +++ b/src/resources/wcag2.yml @@ -2,7 +2,7 @@ id: "wcag20:text-equiv-all" level: "wcag20:level_a" default: "cantTell" - testClusters: + testAggregators: - passed: "cantTell" failed: "failed" tests: @@ -37,7 +37,7 @@ 1.2.2: id: "wcag20:media-equiv-captions" level: "wcag20:level_a" - testClusters: + testAggregators: - passed: "cantTell" failed: "failed" tests: @@ -73,7 +73,7 @@ id: "wcag20:content-structure-separation-programmatic" level: "wcag20:level_a" default: cantTell - testClusters: + testAggregators: - passed: "passed" failed: "failed" type: combined @@ -146,7 +146,7 @@ id: "wcag20:visual-audio-contrast-without-color" level: "wcag20:level_a" default: "cantTell" - testClusters: + testAggregators: - passed: "passed" failed: "failed" tests: @@ -160,7 +160,7 @@ 1.4.3: id: "wcag20:visual-audio-contrast-contrast" level: "wcag20:level_aa" - testClusters: + testAggregators: - passed: passed failed: failed tests: @@ -226,7 +226,7 @@ id: "wcag20:navigation-mechanisms-refs" level: "wcag20:level_a" # default: "cantTell" - testClusters: + testAggregators: # - passed: "passed" # Test doesn't yet exist # failed: "failed" # tests: @@ -240,7 +240,7 @@ 2.4.7: id: "wcag20:navigation-mechanisms-focus-visible" level: "wcag20:level_aa" - testClusters: + testAggregators: - passed: cannotTell # Should also look at outline failed: failed tests: @@ -257,7 +257,7 @@ 3.1.1: id: "wcag20:meaning-doc-lang-id" level: "wcag20:level_a" - testClusters: + testAggregators: - passed: "cantTell" failed: "failed" type: stacking @@ -322,7 +322,7 @@ id: "wcag20:ensure-compat-parses" level: "wcag20:level_a" default: cantTell - testClusters: + testAggregators: - passed: "passed" failed: "failed" tests: @@ -347,7 +347,7 @@ id: "wcag20:ensure-compat-rsv" level: "wcag20:level_a" default: cantTell - testClusters: + testAggregators: - passed: "passed" failed: "failed" tests: From f353a5fc7f06e813d26d8b8c02e283556baf6f5d Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Thu, 23 Oct 2014 15:55:45 +0200 Subject: [PATCH 35/50] Fixed audoMayBePresent --- src/js/custom/audioMayBePresent.js | 84 ++++++++++--------- src/js/lib/wcag2/criterion.js | 2 - .../audioMayBePresent.html | 28 ++++--- 3 files changed, 62 insertions(+), 52 deletions(-) diff --git a/src/js/custom/audioMayBePresent.js b/src/js/custom/audioMayBePresent.js index 7d08e4bd7..db4657003 100644 --- a/src/js/custom/audioMayBePresent.js +++ b/src/js/custom/audioMayBePresent.js @@ -1,7 +1,10 @@ quail.audioMayBePresent=function(quail, test, Case){ + var fileTypes=['mp3', 'm4p', 'ogg', 'oga', 'opus', 'wav', 'wma', 'wv']; function getSource(element){ - var source=false; + if (element.is('a')) { + return element.attr('href'); + } if (element.attr('src') !== undefined) { return element.attr('src'); @@ -10,50 +13,51 @@ quail.audioMayBePresent=function(quail, test, Case){ if (element.find('source').attr('src') !== undefined) { return element.find('source').attr('src'); } - - return source; } - test.get('$scope').each(function(){ - var testableElements=$(this).find('audio, object'); + test.get('$scope').each(function() { + var testableElements=$(this).find('audio, object, a[href]'); + var hasCase = false; - if (testableElements.length === 0) { - var _case=Case({ + testableElements.each(function(){ + var $this = $(this); + var source = getSource($this); + + if ($this.is('object')) { + hasCase = true; + test.add(Case({ + element: this, + expected: $this.closest('.quail-test').data('expected'), + status: 'cantTell' + })); + + } else if (source && $.inArray(source.split('.').pop(), fileTypes) > -1) { + hasCase = true; + test.add(Case({ + element: this, + expected: $this.closest('.quail-test').data('expected'), + status: 'cantTell' + })); + + } else if($this.find('param[name="src"]').attr('value') !== undefined && + $.inArray(source.split('.').pop(), fileTypes) > -1) { + + hasCase = true; + test.add(Case({ + element: this, + expected: $this.closest('.quail-test').data('expected'), + status: 'cantTell' + })); + } + }); + + if (!hasCase) { + test.add(Case({ element: this, status: 'inapplicable', expected: $(this).closest('.quail-test').data('expected') - }); - test.add(_case); + })); } - - testableElements.each(function(){ - var fileTypes=['webm', 'flv', 'ogv', 'ogg', 'avi', 'mov', 'qt', 'wmv', 'asf', 'mp4', 'm4p', 'm4v', 'mpg', 'mp2', 'mpeg', 'mpg', 'mpe', 'mpv', 'm2v', '3gp', '3g2']; - var _case=Case({ - element: this, - expected: $(this).closest('.quail-test').data('expected') - }); - test.add(_case); - - var source = getSource($(this)); - if (source && $.inArray(source.split('.').pop(), fileTypes) > -1) { - _case.set({ - 'status': 'cantTell' - }); - return; - } - if($(this).find('param[name="src"]').attr('value') !== undefined){ - - source = $(this).find('param[name="src"]').attr('value'); - $.each(fileTypes, function(index, item){ - if(source.indexOf("." + item) > -1) { - _case.set({ - 'status': 'cantTell' - }); - return false; - } - }); - return; - } - }); }); -}; + +}; \ No newline at end of file diff --git a/src/js/lib/wcag2/criterion.js b/src/js/lib/wcag2/criterion.js index 85fe95393..d985e87c8 100644 --- a/src/js/lib/wcag2/criterion.js +++ b/src/js/lib/wcag2/criterion.js @@ -32,8 +32,6 @@ quail.lib.wcag2.Criterion = (function () { // Create a precondition test if ($.isArray(data.preconditions)) { var preconditionTest = { - passed: 'inapplicable', - failed: 'cantTell', type: 'stacking', // If any of it's content is found it should return cantTell tests: data.preconditions }; diff --git a/test/accessibility-tests/audioMayBePresent.html b/test/accessibility-tests/audioMayBePresent.html index 453158791..4c8ab30d8 100644 --- a/test/accessibility-tests/audioMayBePresent.html +++ b/test/accessibility-tests/audioMayBePresent.html @@ -3,25 +3,33 @@ audioMayBePresent + -
+
-
+
+ Horses +
+ + + +
+
- - - - - - - +
From f5e0309e6dc88d3bc77efadf05e3f0a6bc3c387d Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Thu, 23 Oct 2014 15:56:29 +0200 Subject: [PATCH 36/50] Update videoMayBePresent-More work needed --- src/js/custom/videoMayBePresent.js | 10 +++++----- test/accessibility-tests/videoMayBePresent.html | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/js/custom/videoMayBePresent.js b/src/js/custom/videoMayBePresent.js index df9994549..2c3eb647e 100644 --- a/src/js/custom/videoMayBePresent.js +++ b/src/js/custom/videoMayBePresent.js @@ -1,17 +1,17 @@ quail.videoMayBePresent=function(quail, test, Case){ function getSource(element){ - var source=false; - - if (element.attr('src') !== undefined) { - return element.attr('src'); + if (element.is('a')) { + return element.attr('href'); } if (element.find('source').attr('src') !== undefined) { return element.find('source').attr('src'); } - return source; + if (element.attr('src') !== undefined) { + return element.attr('src'); + } } test.get('$scope').each(function(){ diff --git a/test/accessibility-tests/videoMayBePresent.html b/test/accessibility-tests/videoMayBePresent.html index c05403e6f..9a19a59b3 100644 --- a/test/accessibility-tests/videoMayBePresent.html +++ b/test/accessibility-tests/videoMayBePresent.html @@ -13,6 +13,7 @@
+
+
From ecb58e1ce326289a2da483195d3b6748107ac34b Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Tue, 2 Dec 2014 14:31:39 +0100 Subject: [PATCH 47/50] Fixed lint problem --- src/js/custom/audioMayBePresent.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/js/custom/audioMayBePresent.js b/src/js/custom/audioMayBePresent.js index 5e467006d..80d63a014 100644 --- a/src/js/custom/audioMayBePresent.js +++ b/src/js/custom/audioMayBePresent.js @@ -7,26 +7,26 @@ quail.audioMayBePresent=function(quail, test, Case){ // Audio is definately an audio, and objects could be too. $this.find('object, audio').each(function () { + hasCase = true; + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'cantTell' + })); + }); + + // Links refering to files with an audio extensions are good indicators too + $this.find('a[href]').each(function () { + var $this = $(this); + var extension = $this.attr('href').split('.').pop(); + if ($.inArray(extension, audioExtensions) !== -1) { hasCase = true; test.add(Case({ element: this, - expected: $(this).closest('.quail-test').data('expected'), + expected: $this.closest('.quail-test').data('expected'), status: 'cantTell' })); - }); - - // Links refering to files with an audio extensions are good indicators too - $this.find('a[href]').each(function () { - var $this = $(this); - var extension = $this.attr('href').split('.').pop(); - if ($.inArray(extension, audioExtensions) !== -1) { - hasCase = true; - test.add(Case({ - element: this, - expected: $this.closest('.quail-test').data('expected'), - status: 'cantTell' - })); - } + } }); // if no case was added, return inapplicable From 4e98248c8fae0494bea8bc0e085555f034d5599f Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Tue, 2 Dec 2014 14:49:11 +0100 Subject: [PATCH 48/50] Removed animated gif precondition from 1.2.1 --- src/resources/wcag2.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/wcag2.yml b/src/resources/wcag2.yml index 423060a10..950ef8cc5 100644 --- a/src/resources/wcag2.yml +++ b/src/resources/wcag2.yml @@ -33,7 +33,6 @@ preconditions: - "videoMayBePresent" - "audioMayBePresent" - - "animatedGifMayBePresent" 1.2.2: id: "wcag20:media-equiv-captions" level: "wcag20:level_a" From 41b351bdeb3201abcb0dace77e650c66b9d40c02 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Tue, 2 Dec 2014 15:33:25 +0100 Subject: [PATCH 49/50] Precon titles updated --- src/resources/preconditions.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/resources/preconditions.yml b/src/resources/preconditions.yml index 5a6369341..73d387658 100644 --- a/src/resources/preconditions.yml +++ b/src/resources/preconditions.yml @@ -2,11 +2,11 @@ videoMayBePresent: type: "custom" testability: 1 title: - en: "Video or object uses a link that points to a file with a video extension" - nl: "Video of object met een link naar een bestand met een video extensie" + en: "The video should be tested if captions, transcript and audio description is required" + nl: "De video dient te worden getest of ondertiteling, transcript en audiodescription nodig zijn" description: - en: "" - nl: "" + en: "The video should be tested if captions, transcript and audio description is required" + nl: "De video dient te worden getest of ondertiteling, transcript en audiodescription nodig zijn" guidelines: [] tags: - "link" @@ -16,11 +16,11 @@ audioMayBePresent: type: "custom" testability: 1 title: - en: "Audio or object uses a link that points to a file with a video extension" - nl: "Audio of object met een link naar een bestand met een video extensie" + en: "The audio should be tested if captions, transcript and audio description is required" + nl: "De audio dient te worden getest of ondertiteling, transcript en audiodescription nodig zijn" description: - en: "" - nl: "" + en: "The audio should be tested if captions, transcript and audio description is required" + nl: "De audio dient te worden getest of ondertiteling, transcript en audiodescription nodig zijn" guidelines: [] tags: - "link" @@ -44,8 +44,8 @@ userInputMayBeRequired: type: "custom" testability: 1 title: - en: "Test if user input is required" - nl: "Test of er invoervelden zijn" + en: "Form should be checked to ensure sufficient feedback is given to the user" + nl: "Formulieren dienen te worden gecontroleerd of voldoende feedback aan gebruikers wordt gegeven" description: en: "" nl: "" From fc815274558842b9fb4d5fa637c469586e06172f Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Wed, 3 Dec 2014 11:03:42 +0100 Subject: [PATCH 50/50] fixed jshint prob --- src/js/components/color.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/components/color.js b/src/js/components/color.js index 813506e9c..18cb1eb88 100644 --- a/src/js/components/color.js +++ b/src/js/components/color.js @@ -151,7 +151,7 @@ quail.components.color = function(quail, test, Case, options) { this.cache[cacheKey] = (element.css('color')) ? element.css('color') : 'rgb(0,0,0)'; return this.cache[cacheKey]; } - + var bcolor = element.css('background-color'); if (this.hasBackgroundColor(bcolor)) { this.cache[cacheKey] = bcolor;