From 22385d0201bc33f6839662da7bdb8904cd73c085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 10:09:38 +0100 Subject: [PATCH 01/25] Don't skip color contrast rule --- test/system/test_case.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index cf737bdb35..5b4cb8dafe 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -16,7 +16,7 @@ class TestCase < ActionDispatch::SystemTestCase # Skip `:region` which relates to preview page structure rather than individual component. # Skip `:color-contrast` which requires primer design-level change. - AXE_RULES_TO_SKIP = [:region, :"color-contrast"].freeze + AXE_RULES_TO_SKIP = [:region].freeze AXE_WITHIN_SELECTOR = "body" def visit_preview(preview_name, params = {}) From fb0ed8284abd0d084d40dac502ab6f64c737cd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 13:30:28 +0100 Subject: [PATCH 02/25] Inject axe-core from node_modules into system tests --- test/system/test_case.rb | 78 ++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index 5b4cb8dafe..1bbc90ecc1 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -16,8 +16,7 @@ class TestCase < ActionDispatch::SystemTestCase # Skip `:region` which relates to preview page structure rather than individual component. # Skip `:color-contrast` which requires primer design-level change. - AXE_RULES_TO_SKIP = [:region].freeze - AXE_WITHIN_SELECTOR = "body" + AXE_RULES_TO_SKIP = [:region, :'color-contrast-enhanced'].freeze def visit_preview(preview_name, params = {}) component_name = self.class.name.gsub("Test", "").gsub("Integration", "") @@ -28,39 +27,80 @@ def visit_preview(preview_name, params = {}) component_uri = component_name.underscore url = +"/rails/view_components/primer/#{status_path}#{component_uri}/#{preview_name}" - query_string = params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&") - url << "?#{query_string}" if query_string.present? + query_string = params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&") + url << "?#{query_string}" if query_string.present? - visit(url) + visit(url) - assert_accessible(page) + assert_accessible end - def assert_accessible(page, within: AXE_WITHIN_SELECTOR, skipping: AXE_RULES_TO_SKIP, **options) - options[:within] = within - options[:skipping] = skipping + def format_accessibility_errors(violations) + results = violations.map.with_index do |summary, index| + summary["nodes"].map do |node| + <<~OUTPUT + #{index + 1}) #{summary["id"]}: #{summary["description"]} (#{summary["impact"]}) + #{summary["helpUrl"]} + The following #{node["any"].size} node violate this rule: + #{node["any"].map do |violation| + items = node["failureSummary"].sub("Fix any of the following:", "").split("\n") + <<~OUTPUT + Selector: #{node["target"].join(', ')} + HTML: #{node["html"]} + Fix any of the following: + #{items.map { |item| "- #{item.strip}" }.join} + OUTPUT + end.join} + OUTPUT + end.join + end.join + <<~OUTPUT + Found #{violations.size} accessibility violations: + #{results} + OUTPUT + end + + def assert_accessible + axe_exists = page.driver.evaluate_script '!!window.axe' + + results = page.driver.evaluate_async_script <<~JS + const callback = arguments[arguments.length - 1]; + #{File.read("node_modules/axe-core/axe.min.js") unless axe_exists} + + const rulesToRun = axe + .getRules(["wcag2a", "wcag21a", "wcag2aa", "wcag2aaa", "wcag21aa", "wcag21aaa"]) + .map(rule => rule.ruleId) + .filter(id => ![#{AXE_RULES_TO_SKIP.map { |id| "'#{id}'" }.join(', ')}].includes(id)) - is_axe_clean = Axe::Matchers::BeAxeClean.new.tap do |a| - options.each do |option| - key, value = option - a.send(key, *value) - end - end + const options = { + elementRef: true, + resultTypes: ['violations', 'incomplete'], + runOnly: { + type: 'rule', + values: rulesToRun + } + } + axe.run(document.body, options).then(res => JSON.parse(JSON.stringify(res))).then(callback); + JS - Axe::AccessibleExpectation.new.assert page, is_axe_clean + violations = (results["violations"] + results["incomplete"].filter { |item| item["impact"] != "serious" && item["impact"] != "critical" }) + + message = format_accessibility_errors(violations) + + assert violations.size == 0, message end # Capybara Overrides to run accessibility checks when UI changes. def fill_in(locator = nil, **kwargs) super - assert_accessible(page) + assert_accessible end def click_button(locator = nil, **kwargs) super - assert_accessible(page) + assert_accessible + end end - end end From 55a35ff0e619cddac4972339b4cd7fc8596115c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 13:38:53 +0100 Subject: [PATCH 03/25] Fix rubocop violations --- test/system/test_case.rb | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index 1bbc90ecc1..fb052f2c49 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -27,10 +27,10 @@ def visit_preview(preview_name, params = {}) component_uri = component_name.underscore url = +"/rails/view_components/primer/#{status_path}#{component_uri}/#{preview_name}" - query_string = params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&") - url << "?#{query_string}" if query_string.present? + query_string = params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&") + url << "?#{query_string}" if query_string.present? - visit(url) + visit(url) assert_accessible end @@ -39,17 +39,17 @@ def format_accessibility_errors(violations) results = violations.map.with_index do |summary, index| summary["nodes"].map do |node| <<~OUTPUT - #{index + 1}) #{summary["id"]}: #{summary["description"]} (#{summary["impact"]}) - #{summary["helpUrl"]} - The following #{node["any"].size} node violate this rule: - #{node["any"].map do |violation| - items = node["failureSummary"].sub("Fix any of the following:", "").split("\n") - <<~OUTPUT - Selector: #{node["target"].join(', ')} - HTML: #{node["html"]} - Fix any of the following: - #{items.map { |item| "- #{item.strip}" }.join} - OUTPUT + #{index + 1}) #{summary['id']}: #{summary['description']} (#{summary['impact']}) + #{summary['helpUrl']} + The following #{node['any'].size} node violate this rule: + #{node['any'].map do |_violation| + items = node['failureSummary'].sub('Fix any of the following:', '').split("\n") + <<~OUTPUT + Selector: #{node['target'].join(', ')} + HTML: #{node['html']} + Fix any of the following: + #{items.map { |item| "- #{item.strip}" }.join} + OUTPUT end.join} OUTPUT end.join @@ -61,11 +61,11 @@ def format_accessibility_errors(violations) end def assert_accessible - axe_exists = page.driver.evaluate_script '!!window.axe' + axe_exists = page.driver.evaluate_script "!!window.axe" results = page.driver.evaluate_async_script <<~JS const callback = arguments[arguments.length - 1]; - #{File.read("node_modules/axe-core/axe.min.js") unless axe_exists} + #{File.read('node_modules/axe-core/axe.min.js') unless axe_exists} const rulesToRun = axe .getRules(["wcag2a", "wcag21a", "wcag2aa", "wcag2aaa", "wcag21aa", "wcag21aaa"]) @@ -87,7 +87,7 @@ def assert_accessible message = format_accessibility_errors(violations) - assert violations.size == 0, message + assert violations.empty?, message end # Capybara Overrides to run accessibility checks when UI changes. From e3768643af99cf1e72ca30852a7bb0140a8ae43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 13:41:53 +0100 Subject: [PATCH 04/25] Fix `AccessibilityTest` --- test/accessibility_test.rb | 3 +-- test/system/test_case.rb | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/accessibility_test.rb b/test/accessibility_test.rb index 01345cae3a..03a26fcaa5 100644 --- a/test/accessibility_test.rb +++ b/test/accessibility_test.rb @@ -23,8 +23,7 @@ class AccessibilityTest < System::TestCase component_previews.each do |preview| define_method(:"test_#{component_uri.parameterize(separator: "_")}_#{preview}") do visit("/rails/view_components/#{component_uri}/#{preview}") - assert_accessible(page) - puts "#{component_uri}##{preview} passed check." + assert_accessible end end end diff --git a/test/system/test_case.rb b/test/system/test_case.rb index fb052f2c49..812b4f07cd 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -16,7 +16,7 @@ class TestCase < ActionDispatch::SystemTestCase # Skip `:region` which relates to preview page structure rather than individual component. # Skip `:color-contrast` which requires primer design-level change. - AXE_RULES_TO_SKIP = [:region, :'color-contrast-enhanced'].freeze + AXE_RULES_TO_SKIP = [:region, :'color-contrast', :'color-contrast-enhanced'].freeze def visit_preview(preview_name, params = {}) component_name = self.class.name.gsub("Test", "").gsub("Integration", "") From b456eaf2c380bbb1b2fd4ffbd1182c98ba34d304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 14:11:58 +0100 Subject: [PATCH 05/25] Match `aria-label` with element text contents --- previews/primer/clipboard_copy_preview.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/previews/primer/clipboard_copy_preview.rb b/previews/primer/clipboard_copy_preview.rb index 6ab610d334..1c8a202c88 100644 --- a/previews/primer/clipboard_copy_preview.rb +++ b/previews/primer/clipboard_copy_preview.rb @@ -23,7 +23,7 @@ def default(value: "Text to copy", aria_label: "Copy text to the system clipboar # # @param aria_label [String] # @param value [String] - def text(value: "Text to copy", aria_label: "Copy text to the system clipboard") + def text(value: "Text to copy", aria_label: "Click to copy!") render(Primer::ClipboardCopy.new(value: value, "aria-label": aria_label)) { "Click to copy!" } end From 0189d13fd3951998efe53f1d8b1bc31086e55ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 14:17:22 +0100 Subject: [PATCH 06/25] Ensure that tooltip text includes element text content --- previews/primer/alpha/tooltip_preview.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/previews/primer/alpha/tooltip_preview.rb b/previews/primer/alpha/tooltip_preview.rb index 99fe1d51f5..d4a37922f1 100644 --- a/previews/primer/alpha/tooltip_preview.rb +++ b/previews/primer/alpha/tooltip_preview.rb @@ -26,7 +26,7 @@ def default(type: :description, direction: :s, tooltip_text: "Tooltip text") # @param direction select [s, n, e, w, ne, nw, se, sw] # @param tooltip_text text - def label_tooltip_on_button_with_existing_labelledby(type: :label, direction: :s, tooltip_text: "Tooltip text") + def label_tooltip_on_button_with_existing_labelledby(type: :label, direction: :s, tooltip_text: "You can press a button") render(Primer::Beta::Button.new(id: "button-with-existing-label", "aria-labelledby": "existing-label-id")) do |c| c.tooltip(text: tooltip_text, type: type, direction: direction) "Button" From 0bf206682586ca4d7a25abf9692114b74459a59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 14:39:21 +0100 Subject: [PATCH 07/25] Fix violations reporting --- test/system/test_case.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index 812b4f07cd..5c3b8d974e 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -38,19 +38,19 @@ def visit_preview(preview_name, params = {}) def format_accessibility_errors(violations) results = violations.map.with_index do |summary, index| summary["nodes"].map do |node| + violations = node["any"] + node["all"] <<~OUTPUT - #{index + 1}) #{summary['id']}: #{summary['description']} (#{summary['impact']}) - #{summary['helpUrl']} - The following #{node['any'].size} node violate this rule: - #{node['any'].map do |_violation| - items = node['failureSummary'].sub('Fix any of the following:', '').split("\n") - <<~OUTPUT - Selector: #{node['target'].join(', ')} - HTML: #{node['html']} - Fix any of the following: - #{items.map { |item| "- #{item.strip}" }.join} - OUTPUT - end.join} + #{index + 1}) #{summary['id']}: #{summary['description']} (#{summary['impact']}) + #{summary['helpUrl']} + #{' '} + The following #{violations.size} node violate this rule: + #{violations.map do |_violation| + <<~OUTPUT + Selector: #{node['target'].join(', ')} + HTML: #{node['html']} + #{node['failureSummary']} + OUTPUT + end.join} OUTPUT end.join end.join From 3cf50ef6b9e7d66c36737c9e77a9365d75de036f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 14:51:43 +0100 Subject: [PATCH 08/25] Add surrounding text to link previews for contrast checks --- previews/primer/alpha/tooltip_preview.rb | 9 ++++++--- previews/primer/beta/link_preview.rb | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/previews/primer/alpha/tooltip_preview.rb b/previews/primer/alpha/tooltip_preview.rb index d4a37922f1..37b596ecf4 100644 --- a/previews/primer/alpha/tooltip_preview.rb +++ b/previews/primer/alpha/tooltip_preview.rb @@ -77,9 +77,12 @@ def tooltip_with_button(type: :description, direction: :s, tooltip_text: "Toolti # @label Tooltip with Primer::Beta::Link def tooltip_with_link(type: :description, direction: :s, tooltip_text: "Tooltip text") - render(Primer::Beta::Link.new(href: "#link-with-tooltip", id: "link-with-tooltip")) do |c| - c.tooltip(text: tooltip_text, type: type, direction: direction) - "Button" + render(Primer::BaseComponent.new(tag: :div)) do + "Here is some surrounding text." + render(Primer::Beta::Link.new(href: "#link-with-tooltip", id: "link-with-tooltip")) do |c| + c.tooltip(text: tooltip_text, type: type, direction: direction) + "Button" + end end end diff --git a/previews/primer/beta/link_preview.rb b/previews/primer/beta/link_preview.rb index 9ee83a74ac..00f90196ea 100644 --- a/previews/primer/beta/link_preview.rb +++ b/previews/primer/beta/link_preview.rb @@ -31,9 +31,12 @@ def default(tag: :a, scheme: :default, muted: false, underline: true) # @param tag [Symbol] select [a, span] # @param scheme [Symbol] select [default, primary, secondary] def tooltip(tag: :a, scheme: :default, muted: false, underline: true) - render(Primer::Beta::Link.new(href: "#", id: "tooltip-link", tag: tag, scheme: scheme, muted: muted, underline: underline)) do |component| - component.with_tooltip(text: "Tooltip text") - "Link with tooltip" + render(Primer::BaseComponent.new(tag: :div)) do + "First there's some surrounding text." + render(Primer::Beta::Link.new(href: "#", id: "tooltip-link", tag: tag, scheme: scheme, muted: muted, underline: underline)) do |component| + component.with_tooltip(text: "Tooltip text") + "Link with tooltip" + end end end end From 8cb08c905ba7da9485de27c24546ae8eee7b7811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 15:54:42 +0100 Subject: [PATCH 09/25] Output path when assertion fails --- test/accessibility_test.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/accessibility_test.rb b/test/accessibility_test.rb index 03a26fcaa5..3dafa22047 100644 --- a/test/accessibility_test.rb +++ b/test/accessibility_test.rb @@ -23,7 +23,12 @@ class AccessibilityTest < System::TestCase component_previews.each do |preview| define_method(:"test_#{component_uri.parameterize(separator: "_")}_#{preview}") do visit("/rails/view_components/#{component_uri}/#{preview}") - assert_accessible + begin + assert_accessible + rescue e + puts "/rails/view_components/#{component_uri}/#{preview}" + raise e + end end end end From 22987f7b844a2a7698830746036d67694a5bae9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 15:57:29 +0100 Subject: [PATCH 10/25] Add a changeset --- .changeset/healthy-lamps-bow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/healthy-lamps-bow.md diff --git a/.changeset/healthy-lamps-bow.md b/.changeset/healthy-lamps-bow.md new file mode 100644 index 0000000000..95086359b5 --- /dev/null +++ b/.changeset/healthy-lamps-bow.md @@ -0,0 +1,5 @@ +--- +'@primer/view-components': patch +--- + +Update `axe-core` scanning in system tests From c00be25c267750cbee329eb2ac0e8e406a2b5f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 15:59:57 +0100 Subject: [PATCH 11/25] Rescue error correctly --- test/accessibility_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/accessibility_test.rb b/test/accessibility_test.rb index 3dafa22047..31f41cba6e 100644 --- a/test/accessibility_test.rb +++ b/test/accessibility_test.rb @@ -25,7 +25,7 @@ class AccessibilityTest < System::TestCase visit("/rails/view_components/#{component_uri}/#{preview}") begin assert_accessible - rescue e + rescue StandardError => e puts "/rails/view_components/#{component_uri}/#{preview}" raise e end From 07bec9c223e2a419888f030fee154b856bcf23ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 16:35:48 +0100 Subject: [PATCH 12/25] Explicitly install `axe-core@4.5` --- package-lock.json | 14 +++++++------- package.json | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 415d81aa44..35542312c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "@rollup/plugin-typescript": "^8.3.3", "@typescript-eslint/eslint-plugin": "^5.31.0", "@typescript-eslint/parser": "^5.31.0", + "axe-core": "^4.5.1", "chokidar-cli": "^3.0.0", "cssnano": "^5.1.13", "eslint": "^8.23.1", @@ -1651,11 +1652,10 @@ } }, "node_modules/axe-core": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", - "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.1.tgz", + "integrity": "sha512-1exVbW0X1O/HSr/WMwnaweyqcWOgZgLiVxdLG34pvSQk4NlYQr9OUy0JLwuhFfuVNQzzqgH57eYzkFBCb3bIsQ==", "dev": true, - "license": "MPL-2.0", "engines": { "node": ">=4" } @@ -10419,9 +10419,9 @@ } }, "axe-core": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", - "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.1.tgz", + "integrity": "sha512-1exVbW0X1O/HSr/WMwnaweyqcWOgZgLiVxdLG34pvSQk4NlYQr9OUy0JLwuhFfuVNQzzqgH57eYzkFBCb3bIsQ==", "dev": true }, "axobject-query": { diff --git a/package.json b/package.json index fdff2427a0..5f2d7e8037 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@rollup/plugin-typescript": "^8.3.3", "@typescript-eslint/eslint-plugin": "^5.31.0", "@typescript-eslint/parser": "^5.31.0", + "axe-core": "^4.5.1", "chokidar-cli": "^3.0.0", "cssnano": "^5.1.13", "eslint": "^8.23.1", From ff171b9044e85a34b6f70b8de1e746655725b6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 16:36:19 +0100 Subject: [PATCH 13/25] Skip `aria-required-children` which is broken --- test/system/test_case.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index 5c3b8d974e..330791be83 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -16,7 +16,8 @@ class TestCase < ActionDispatch::SystemTestCase # Skip `:region` which relates to preview page structure rather than individual component. # Skip `:color-contrast` which requires primer design-level change. - AXE_RULES_TO_SKIP = [:region, :'color-contrast', :'color-contrast-enhanced'].freeze + # Skip `:aria-required-children` is broken in 4.5: https://github.com/dequelabs/axe-core/issues/3758 + AXE_RULES_TO_SKIP = [:region, :'color-contrast', :'color-contrast-enhanced', :'aria-required-children'].freeze def visit_preview(preview_name, params = {}) component_name = self.class.name.gsub("Test", "").gsub("Integration", "") From bbcf2937a3ddc2c4745269bfd16feadfafdef8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 16:52:20 +0100 Subject: [PATCH 14/25] Skip anouther rule --- test/system/test_case.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index 330791be83..fddf0d3c35 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -17,7 +17,8 @@ class TestCase < ActionDispatch::SystemTestCase # Skip `:region` which relates to preview page structure rather than individual component. # Skip `:color-contrast` which requires primer design-level change. # Skip `:aria-required-children` is broken in 4.5: https://github.com/dequelabs/axe-core/issues/3758 - AXE_RULES_TO_SKIP = [:region, :'color-contrast', :'color-contrast-enhanced', :'aria-required-children'].freeze + # Skip `:link-in-text-block` which is new and seems broken. + AXE_RULES_TO_SKIP = [:region, :'color-contrast', :'color-contrast-enhanced', :'aria-required-children', :'link-in-text-block'].freeze def visit_preview(preview_name, params = {}) component_name = self.class.name.gsub("Test", "").gsub("Integration", "") From ec1ccd450849e87448d081755fa2404e450ec040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 17:56:04 +0100 Subject: [PATCH 15/25] Revert some things --- previews/primer/alpha/tooltip_preview.rb | 23 ++++++++++------------- previews/primer/beta/link_preview.rb | 9 +++------ test/accessibility_test.rb | 8 ++------ test/system/alpha/tooltip_test.rb | 2 +- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/previews/primer/alpha/tooltip_preview.rb b/previews/primer/alpha/tooltip_preview.rb index 37b596ecf4..2976852c41 100644 --- a/previews/primer/alpha/tooltip_preview.rb +++ b/previews/primer/alpha/tooltip_preview.rb @@ -7,7 +7,7 @@ class TooltipPreview < ViewComponent::Preview # @param type [Symbol] select [["Description", description], ["Label", label]] # @param direction select [s, n, e, w, ne, nw, se, sw] # @param tooltip_text text - def playground(type: :description, direction: :s, tooltip_text: "Tooltip text") + def playground(type: :description, direction: :s, tooltip_text: "You can press a button") render(Primer::Beta::Button.new(id: "button-with-tooltip")) do |c| c.tooltip(text: tooltip_text, type: type, direction: direction) "Button" @@ -17,7 +17,7 @@ def playground(type: :description, direction: :s, tooltip_text: "Tooltip text") # @param type [Symbol] select [["Description", description], ["Label", label]] # @param direction select [s, n, e, w, ne, nw, se, sw] # @param tooltip_text text - def default(type: :description, direction: :s, tooltip_text: "Tooltip text") + def default(type: :description, direction: :s, tooltip_text: "You can press a button") render(Primer::Beta::Button.new(id: "button-with-tooltip")) do |c| c.tooltip(text: tooltip_text, type: type, direction: direction) "Button" @@ -35,7 +35,7 @@ def label_tooltip_on_button_with_existing_labelledby(type: :label, direction: :s # @param direction select [s, n, e, w, ne, nw, se, sw] # @param tooltip_text text - def description_tooltip_on_button_with_existing_describedby(type: :description, direction: :s, tooltip_text: "Tooltip text") + def description_tooltip_on_button_with_existing_describedby(type: :description, direction: :s, tooltip_text: "You can press a button") render(Primer::Beta::Button.new(id: "button-with-existing-description", "aria-describedby": "existing-description-id")) do |c| c.tooltip(text: tooltip_text, type: type, direction: direction) "Button" @@ -56,7 +56,7 @@ def with_right_most_position(type: :description, direction: :s, tooltip_text: "A # @param direction select [s, n, e, w, ne, nw, se, sw] # @param tooltip_text text - def with_multiple_on_a_page(type: :description, direction: :s, tooltip_text: "Tooltip text") + def with_multiple_on_a_page(type: :description, direction: :s, tooltip_text: "You can press a button") render_with_template( locals: { type: type, @@ -68,7 +68,7 @@ def with_multiple_on_a_page(type: :description, direction: :s, tooltip_text: "To # @!group Tooltip enabled elements # @label Tooltip with Primer::Beta::Button - def tooltip_with_button(type: :description, direction: :s, tooltip_text: "Tooltip text") + def tooltip_with_button(type: :description, direction: :s, tooltip_text: "You can press a button") render(Primer::Beta::Button.new(id: "button-with-tooltip")) do |c| c.tooltip(text: tooltip_text, type: type, direction: direction) "Button" @@ -76,18 +76,15 @@ def tooltip_with_button(type: :description, direction: :s, tooltip_text: "Toolti end # @label Tooltip with Primer::Beta::Link - def tooltip_with_link(type: :description, direction: :s, tooltip_text: "Tooltip text") - render(Primer::BaseComponent.new(tag: :div)) do - "Here is some surrounding text." - render(Primer::Beta::Link.new(href: "#link-with-tooltip", id: "link-with-tooltip")) do |c| - c.tooltip(text: tooltip_text, type: type, direction: direction) - "Button" - end + def tooltip_with_link(type: :description, direction: :s, tooltip_text: "You can press a button") + render(Primer::Beta::Link.new(href: "#link-with-tooltip", id: "link-with-tooltip")) do |c| + c.tooltip(text: tooltip_text, type: type, direction: direction) + "Button" end end # @label Tooltip with Primer::IconButton - def tooltip_with_icon_button(direction: :s, tooltip_text: "Tooltip text") + def tooltip_with_icon_button(direction: :s, tooltip_text: "You can press a button") render(Primer::Beta::IconButton.new(icon: :search, "aria-label": tooltip_text, tooltip_direction: direction)) end # @!endgroup diff --git a/previews/primer/beta/link_preview.rb b/previews/primer/beta/link_preview.rb index 00f90196ea..9ee83a74ac 100644 --- a/previews/primer/beta/link_preview.rb +++ b/previews/primer/beta/link_preview.rb @@ -31,12 +31,9 @@ def default(tag: :a, scheme: :default, muted: false, underline: true) # @param tag [Symbol] select [a, span] # @param scheme [Symbol] select [default, primary, secondary] def tooltip(tag: :a, scheme: :default, muted: false, underline: true) - render(Primer::BaseComponent.new(tag: :div)) do - "First there's some surrounding text." - render(Primer::Beta::Link.new(href: "#", id: "tooltip-link", tag: tag, scheme: scheme, muted: muted, underline: underline)) do |component| - component.with_tooltip(text: "Tooltip text") - "Link with tooltip" - end + render(Primer::Beta::Link.new(href: "#", id: "tooltip-link", tag: tag, scheme: scheme, muted: muted, underline: underline)) do |component| + component.with_tooltip(text: "Tooltip text") + "Link with tooltip" end end end diff --git a/test/accessibility_test.rb b/test/accessibility_test.rb index 31f41cba6e..01345cae3a 100644 --- a/test/accessibility_test.rb +++ b/test/accessibility_test.rb @@ -23,12 +23,8 @@ class AccessibilityTest < System::TestCase component_previews.each do |preview| define_method(:"test_#{component_uri.parameterize(separator: "_")}_#{preview}") do visit("/rails/view_components/#{component_uri}/#{preview}") - begin - assert_accessible - rescue StandardError => e - puts "/rails/view_components/#{component_uri}/#{preview}" - raise e - end + assert_accessible(page) + puts "#{component_uri}##{preview} passed check." end end end diff --git a/test/system/alpha/tooltip_test.rb b/test/system/alpha/tooltip_test.rb index ebd53a305b..0aef72b57d 100644 --- a/test/system/alpha/tooltip_test.rb +++ b/test/system/alpha/tooltip_test.rb @@ -8,7 +8,7 @@ def test_renders visit_preview(:default) assert_selector("button[id='button-with-tooltip']") - assert_selector("tool-tip[for='button-with-tooltip'][data-view-component][role='tooltip']", text: "Tooltip text", visible: :hidden) + assert_selector("tool-tip[for='button-with-tooltip'][data-view-component][role='tooltip']", text: "You can press a button", visible: :hidden) assert_equal(find("button")["aria-describedby"], find("tool-tip", visible: :hidden)["id"]) end From a7731f655db39ad313adff924f3c8c36fa2213f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 17:59:08 +0100 Subject: [PATCH 16/25] Remove variable from `assert_accessible` again. --- test/accessibility_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/accessibility_test.rb b/test/accessibility_test.rb index 01345cae3a..af300aff12 100644 --- a/test/accessibility_test.rb +++ b/test/accessibility_test.rb @@ -23,7 +23,7 @@ class AccessibilityTest < System::TestCase component_previews.each do |preview| define_method(:"test_#{component_uri.parameterize(separator: "_")}_#{preview}") do visit("/rails/view_components/#{component_uri}/#{preview}") - assert_accessible(page) + assert_accessible puts "#{component_uri}##{preview} passed check." end end From 77135a94aeb1d8f7c83a2dd76ce7492c863f6f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 18:10:45 +0100 Subject: [PATCH 17/25] Remove `axe-core-api` gem --- Gemfile.lock | 33 --------------------------------- primer_view_components.gemspec | 1 - 2 files changed, 34 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 67448d5f1a..875de3aa61 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,16 +41,6 @@ GEM allocation_tracer (0.6.3) ansi (1.5.0) ast (2.4.2) - axe-core-api (4.2.1) - capybara - dumb_delegator - selenium-webdriver - virtus - watir - axiom-types (0.1.1) - descendants_tracker (~> 0.0.4) - ice_nine (~> 0.11.0) - thread_safe (~> 0.3, >= 0.3.1) benchmark-ips (2.8.4) better_html (2.0.1) actionview (>= 6.0) @@ -71,11 +61,8 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - childprocess (4.1.0) cliver (0.3.2) coderay (1.1.3) - coercible (1.0.0) - descendants_tracker (~> 0.0.1) concurrent-ruby (1.1.10) crack (0.4.5) rexml @@ -85,10 +72,7 @@ GEM cuprite (0.13) capybara (>= 2.1, < 4) ferrum (~> 0.11.0) - descendants_tracker (0.0.4) - thread_safe (~> 0.3, >= 0.3.1) docile (1.4.0) - dumb_delegator (1.0.0) erb_lint (0.2.0) activesupport better_html (>= 2.0.1) @@ -109,7 +93,6 @@ GEM htmlentities (4.3.4) i18n (1.12.0) concurrent-ruby (~> 1.0) - ice_nine (0.11.2) listen (3.7.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -204,12 +187,6 @@ GEM rack (>= 1.1) rubocop (>= 0.87.0) ruby-progressbar (1.11.0) - rubyzip (2.3.2) - selenium-webdriver (4.4.0) - childprocess (>= 0.5, < 5.0) - rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2, < 3.0) - websocket (~> 1.0) semantic_range (3.0.0) simplecov (0.21.2) docile (~> 1.1) @@ -232,7 +209,6 @@ GEM terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) thor (1.2.1) - thread_safe (0.3.6) timecop (0.9.5) tzinfo (2.0.5) concurrent-ruby (~> 1.0) @@ -241,13 +217,6 @@ GEM activesupport (>= 5.0.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) - virtus (2.0.0) - axiom-types (~> 0.1) - coercible (~> 1.0) - descendants_tracker (~> 0.0, >= 0.0.3) - watir (7.1.0) - regexp_parser (>= 1.2, < 3) - selenium-webdriver (~> 4.0) webmock (3.18.1) addressable (>= 2.8.0) crack (>= 0.3.2) @@ -258,7 +227,6 @@ GEM railties (>= 5.2) semantic_range (>= 2.3.0) webrick (1.7.0) - websocket (1.2.9) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -278,7 +246,6 @@ DEPENDENCIES activesupport (= 7.0.3) allocation_stats (~> 0.1) allocation_tracer (~> 0.6.3) - axe-core-api (~> 4.2.0) benchmark-ips (~> 2.8.4) bootsnap (>= 1.4.2) capybara (~> 3) diff --git a/primer_view_components.gemspec b/primer_view_components.gemspec index b6f68be73c..6b7297d557 100644 --- a/primer_view_components.gemspec +++ b/primer_view_components.gemspec @@ -34,7 +34,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "allocation_stats", "~> 0.1" spec.add_development_dependency "allocation_tracer", "~> 0.6.3" - spec.add_development_dependency "axe-core-api", "~> 4.2.0" spec.add_development_dependency "benchmark-ips", "~> 2.8.4" spec.add_development_dependency "capybara", "~> 3" spec.add_development_dependency "cuprite", "= 0.13" From 9e5d4b02bc084c929c55512d7a2b4db8bceb2a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Nov 2022 18:16:26 +0100 Subject: [PATCH 18/25] Remove old `require` statements --- test/system/test_case.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index fddf0d3c35..1b1bd50214 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -4,9 +4,6 @@ require "capybara/rails" require "capybara/minitest" -require "axe/matchers/be_axe_clean" -require "axe/expectation" - require "test_helpers/cuprite_setup" require "test_helpers/retry" From aa32f066da86da5a361bb1a95908ce49a6c0e478 Mon Sep 17 00:00:00 2001 From: Cameron Dutro Date: Thu, 19 Jan 2023 10:31:35 -0800 Subject: [PATCH 19/25] Fix formatting in system/test_case.rb --- test/system/test_case.rb | 51 +++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index 1b1bd50214..fcd867e7c3 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -15,7 +15,13 @@ class TestCase < ActionDispatch::SystemTestCase # Skip `:color-contrast` which requires primer design-level change. # Skip `:aria-required-children` is broken in 4.5: https://github.com/dequelabs/axe-core/issues/3758 # Skip `:link-in-text-block` which is new and seems broken. - AXE_RULES_TO_SKIP = [:region, :'color-contrast', :'color-contrast-enhanced', :'aria-required-children', :'link-in-text-block'].freeze + AXE_RULES_TO_SKIP = %i[ + region + color-contrast + color-contrast-enhanced + aria-required-children + link-in-text-block + ].freeze def visit_preview(preview_name, params = {}) component_name = self.class.name.gsub("Test", "").gsub("Integration", "") @@ -35,34 +41,33 @@ def visit_preview(preview_name, params = {}) end def format_accessibility_errors(violations) - results = violations.map.with_index do |summary, index| + results = violations.flat_map.with_index do |summary, index| summary["nodes"].map do |node| violations = node["any"] + node["all"] - <<~OUTPUT - #{index + 1}) #{summary['id']}: #{summary['description']} (#{summary['impact']}) - #{summary['helpUrl']} - #{' '} - The following #{violations.size} node violate this rule: - #{violations.map do |_violation| - <<~OUTPUT - Selector: #{node['target'].join(', ')} - HTML: #{node['html']} - #{node['failureSummary']} - OUTPUT - end.join} - OUTPUT - end.join - end.join + eval(Erubi::Engine.new(<<~ERB, trim: true).src) + <%= index + 1 %>) <%= summary["id"] %>: <%= summary["description"] %> (<%= summary["impact"] %>) + <%= summary["helpUrl"] %> + + The following <%= violations.size %> <%= violations.size == 1 ? "node violates" : "nodes violate" %> this rule: + <% violations.each do |_violation| %> + Selector: <%= node["target"].join(", ") %> + HTML: <%= node["html"] %> + <%= node["failureSummary"] %> + <% end %> + ERB + end + end + <<~OUTPUT Found #{violations.size} accessibility violations: - #{results} + #{results.join} OUTPUT end def assert_accessible - axe_exists = page.driver.evaluate_script "!!window.axe" + axe_exists = page.driver.evaluate_script("!!window.axe") - results = page.driver.evaluate_async_script <<~JS + results = page.driver.evaluate_async_script(<<~JS) const callback = arguments[arguments.length - 1]; #{File.read('node_modules/axe-core/axe.min.js') unless axe_exists} @@ -82,7 +87,9 @@ def assert_accessible axe.run(document.body, options).then(res => JSON.parse(JSON.stringify(res))).then(callback); JS - violations = (results["violations"] + results["incomplete"].filter { |item| item["impact"] != "serious" && item["impact"] != "critical" }) + violations = results["violations"] + results["incomplete"].select do |item| + item["impact"] != "serious" && item["impact"] != "critical" + end message = format_accessibility_errors(violations) @@ -101,5 +108,5 @@ def click_button(locator = nil, **kwargs) assert_accessible end - end + end end From a9797c554d70f95a0308ce75525eac8f1ffc938d Mon Sep 17 00:00:00 2001 From: Cameron Dutro Date: Thu, 19 Jan 2023 11:05:16 -0800 Subject: [PATCH 20/25] Make Rubocop happy --- previews/primer/clipboard_copy_preview.rb | 6 +++--- test/system/test_case.rb | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/previews/primer/clipboard_copy_preview.rb b/previews/primer/clipboard_copy_preview.rb index 1c8a202c88..813158a8e7 100644 --- a/previews/primer/clipboard_copy_preview.rb +++ b/previews/primer/clipboard_copy_preview.rb @@ -8,7 +8,7 @@ class ClipboardCopyPreview < ViewComponent::Preview # @param aria_label [String] # @param value [String] def playground(value: "Text to copy", aria_label: "Copy text to the system clipboard") - render(Primer::ClipboardCopy.new(value: value, "aria-label": aria_label)) + render(Primer::Beta::ClipboardCopy.new(value: value, "aria-label": aria_label)) end # @label Default Options @@ -16,7 +16,7 @@ def playground(value: "Text to copy", aria_label: "Copy text to the system clipb # @param aria_label [String] # @param value [String] def default(value: "Text to copy", aria_label: "Copy text to the system clipboard") - render(Primer::ClipboardCopy.new(value: value, "aria-label": aria_label)) + render(Primer::Beta::ClipboardCopy.new(value: value, "aria-label": aria_label)) end # @label With text instead of icons @@ -24,7 +24,7 @@ def default(value: "Text to copy", aria_label: "Copy text to the system clipboar # @param aria_label [String] # @param value [String] def text(value: "Text to copy", aria_label: "Click to copy!") - render(Primer::ClipboardCopy.new(value: value, "aria-label": aria_label)) { "Click to copy!" } + render(Primer::Beta::ClipboardCopy.new(value: value, "aria-label": aria_label)) { "Click to copy!" } end # @label Copying from an element diff --git a/test/system/test_case.rb b/test/system/test_case.rb index fcd867e7c3..93e4346fde 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -41,9 +41,11 @@ def visit_preview(preview_name, params = {}) end def format_accessibility_errors(violations) + # rubocop:disable Lint/UnusedBlockArgument results = violations.flat_map.with_index do |summary, index| summary["nodes"].map do |node| violations = node["any"] + node["all"] + # rubocop:disable Security/Eval eval(Erubi::Engine.new(<<~ERB, trim: true).src) <%= index + 1 %>) <%= summary["id"] %>: <%= summary["description"] %> (<%= summary["impact"] %>) <%= summary["helpUrl"] %> @@ -55,8 +57,10 @@ def format_accessibility_errors(violations) <%= node["failureSummary"] %> <% end %> ERB + # rubocop:enable Security/Eval end end + # rubocop:enable Lint/UnusedBlockArgument <<~OUTPUT Found #{violations.size} accessibility violations: From 0546173d4818ec0fb8facad9f3fe61a13e0029ad Mon Sep 17 00:00:00 2001 From: Cameron Dutro Date: Thu, 19 Jan 2023 11:06:53 -0800 Subject: [PATCH 21/25] Remove file re-introduced by bad merge --- previews/primer/clipboard_copy_preview.rb | 37 ----------------------- 1 file changed, 37 deletions(-) delete mode 100644 previews/primer/clipboard_copy_preview.rb diff --git a/previews/primer/clipboard_copy_preview.rb b/previews/primer/clipboard_copy_preview.rb deleted file mode 100644 index 813158a8e7..0000000000 --- a/previews/primer/clipboard_copy_preview.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module Primer - # @label ClipboardCopy - class ClipboardCopyPreview < ViewComponent::Preview - # @label Playground - # - # @param aria_label [String] - # @param value [String] - def playground(value: "Text to copy", aria_label: "Copy text to the system clipboard") - render(Primer::Beta::ClipboardCopy.new(value: value, "aria-label": aria_label)) - end - - # @label Default Options - # - # @param aria_label [String] - # @param value [String] - def default(value: "Text to copy", aria_label: "Copy text to the system clipboard") - render(Primer::Beta::ClipboardCopy.new(value: value, "aria-label": aria_label)) - end - - # @label With text instead of icons - # - # @param aria_label [String] - # @param value [String] - def text(value: "Text to copy", aria_label: "Click to copy!") - render(Primer::Beta::ClipboardCopy.new(value: value, "aria-label": aria_label)) { "Click to copy!" } - end - - # @label Copying from an element - # - # @param aria_label [String] - def element(aria_label: "Copy text to the system clipboard") - render_with_template(locals: { aria_label: aria_label }) - end - end -end From 9d403603130c680605685411ea5697d3dfcf74ea Mon Sep 17 00:00:00 2001 From: camertron Date: Thu, 19 Jan 2023 19:10:53 +0000 Subject: [PATCH 22/25] Generating component snapshots --- .../primer/alpha/tooltip/focused.png | Bin 2079 -> 2083 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.playwright/screenshots/previews.test.ts-snapshots/primer/alpha/tooltip/focused.png b/.playwright/screenshots/previews.test.ts-snapshots/primer/alpha/tooltip/focused.png index 4b0860e199df5b295071030cda631f83e113d14b..b38e2f64a2daefc08b435b9c195c2833b24f3de1 100644 GIT binary patch literal 2083 zcmb7Fdpy&7AOA@mmGGSBP!CJ$Wtz&A5eX%aOG_@fl>2mq7$#HFwC!BdIZn?oT`Vji zX6*6#Sk zF6;bp1Z%zX0?RP~=r(~+Qkw#EX5htFnn*ce|H!3WLhvsAS?y;0Rxr3P%suIF((|yn zU%a6ct`yz!!`TZtIpxsbm~(0mU#X{3uy@$pDjJ8trf*S2@jV2QIa}1HzW0F*7!t9_ z!&j&vrv`L^9WugN<2wLkWGC$*f_&HZoz@^0e!t@|h#p1?e*>{79oP+`=Vf5042Z{% z{(tIT1#X6SHgzCa5$fvntHj&h{eH|RCmfO+Zq!AFV<+)eF#|t*W?HF&J|f(lUb^b5 zH(vZ`(Y^8RtsbT3uK%%+aI;lykBqUbMt`ZK=){^nC|Q0*Zx2xyZ+X6IKaEv*#2?%9 z3r`af&03lFX^uO<&g5jGb$nC#GqkRjpTfl}>)I@R!XQ4AV^hYpT}xT> zMQ6Y0frz*tvAw_udiLI@$*|6f>ncnIt$l{HxOWUIVLDskX%Y3{>jSP~NvgztDTa{|^trT0dqYdkBh@%bJh53#`*ji2;q6ZQ!)1UHN&BWwXt}Pm3RGYzl$Xp8Ie?0qEL>j zi{j>Qo6a#JxA#FzO>$k`^Ql{Ead(JJ5_dVq>5Ey*o{p({Te)Qv^kIZ4% znWl7Q1_6mbHBp(Y4eJunnyKP<{wazrF&9}l38*F~aTTMdnVytSgC8}GCW#Rh?fYLyrH#V+)1!@X zY!wbF1C5XE?;t5Zuz5$aINywuFdsSOPuHzE^*}I9sVv&ob#k72fwOl@Lj1fyfE}Ac z1xx#sDzov|^PMtyLpmCupP|d#Rp2t&_oy>-F^-p|zE>8*S=>SeE=tOk=y~IWBZLs% z)-Jl3gq-7*`ZmM~o*IQxQ?U&3sEL^w3^~xU7T|n-u@8#ml=&kfW)FG0qEKY9qRh0l zo<16|oSy34klyNmB6;zl!H4iS?48fdRgai$P*(&TT4BvI1OMlxb-c6p@2y3}FJOl; zFKk|>1go&fQ2&NlB%5mGL!}8u=z6O1t~{+TfVW!VRytc16cN#_#D$jOGWzTrwL_VV=2I5P4huZkq9?Q*Pa5&U6k}0Ha?^%b`x3$xS0&j{ zPJ?b!>ai3UIQxjn#r0P5DMGk26M3vK5-Th4sEyT zh4i2e@1?KK=s1o!R!+#9-1^nA2EVK(6dT%=7s`{LHgsHta-&s1)$9-I+Cay_L9@+$ zW!;bHqM4~i2m<@%`BUGZ;{Nv==$Jk@3;bU&t~U)*UGV+S9153B@|S~lT4MoeAMx}v zRnMM&pSKz$`l0KmAf*Bftsy2EOdEiBl7V_&q|50U9;1xu$QpPR7Ww*WslliPtWiSi z{uMQ!TBSCBO?V3w4othG^)-FwqzL`Lw?U*9pmR6c$B@3VTx+QZYQ^5n8k)CNvpfU; z2=nWWNm%T^R@?ttuH6=%h63;=Uz{k?6x0s+J3wwqfc4(>I_hru8Y!iDDvrb284_D- z(AG`=(*3}B_?J@HS82iPW3rXy1~-+wd?t$KS&9;M9s??U2BhV@+L5SIco)oULZWT1la-UR4_3Rk5M&%8^J*-x#bKZf|BJPX3iZ&kXW9Q=1S%ozoNdc&0)PBB>PD(G delta 1893 zcmXw4d0bOh7JeyKIvt9%!cRp=g+WCuL)m3V1vRAs6hT=cP+3IOIALEx7#+|;*dmJ( z2qG9QTi8+z5JF{y34uI?u!TTa6Obe#2$7JG$z!JXpLc%mJIi;^x%azOS`}LP=C}!j z+v&@RW#XxnMozeX-$&5SPOml}fX z+Bru%HX8;%ZpSm1)i+I~r=XbC%9~$joT(glFZF9(IQ;0q;&R5yPv$^(ZACc*z8Q=G129~!iI6;4(Hv39Lv5?i6QP6b|C+^?)<2)Q2Brv=BHd*lm?qm_0 zS7{e1_umqA|MUCkoW6=gLp7Nc?ADJT7gJDVI4YcKlX;D37Z`spna@a29__9_KVYU!&W3Px&O6gd#kv^;v}3C%RV?n;oM&?PTEU>+FA{JZeZOjxlptd~;)nu{}3nJqBrslCF zU~8<(eCLiQfipq{xy96a&@@R*&7*rN#cg17IBTtUZLvOYFtG6ml}cTnOz0T;Mg!`R zPF)?WF=^wy70$+r+*E14u`U$$p0EwSrjYAd$Y+e0=`gjmwn!3^(AwG>Ci`@V*vUBi zp#6u`jgiri&&sj;>K}^B6LbqzV=+L}smG8=_O;U zXUhT;^BguG2T8M;w2;^TmZHWuoQ308Pz9NpWc;a5jdsb#bd1+LZTR z>kPwvqmzPxl1E0YH0MSmHLCR_$5>AO8O3RN@Qd?sA)l#u|7--D45m8`NaYv zUYGXuR^-X)fRZrQv>F7P1ipjaEpc8cdA$CU$Ge_ZYvomWJRv6q!hj<35+-i03-!pz z2aao53gwp8!h=X*^Kb}seSX3o$aQLr+=P1*C(JsF_M@W*%4pF6=$v}L7(oNGz}6~^ z&9k0JalPFOAHUV|r!tMGWb{l^wiTUrtZxSsNxLuvZK;6>myDbBSk6NQ2y~c z!k+GL$j)4qq_(Rhwa#Omd#OIa-@eT&UhG4hXK;>2>Tzm$^N@4*1CuCV4`i8SGbmpc ztm@#XTR@`J$uefv{Xj{|?s0f|LEP`;YA)%Dj5zjlXZ41xpJiq`Zv{npCvin)0ROO( z489sOgF$1RapHynsc8A6>mDd^I+98QKA--GR{M6huQoS*V5?Pg{Jn-pR47n)#Yc5W zpI^PvOP4Orx;U2Nge05+(xasK7iQLIZ6qk7_2uIarg`T&uRX-%?e?DN8w&(=h9Ul5 z8tX4`+`9Z*#LbvZ{s$IK-i%ltYeWD44M1w^0}N}~g5$PS=wamu??;4=lsxLjjb fb58;JJROoBX!%k1ocZ0>U134Ed%8883Ay)Q67aE# From 294f5199fee2d91abeb79042a206dfc415e5c4a6 Mon Sep 17 00:00:00 2001 From: Cameron Dutro Date: Thu, 19 Jan 2023 11:21:19 -0800 Subject: [PATCH 23/25] Fix clipboard copy preview --- previews/primer/beta/clipboard_copy_preview.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/previews/primer/beta/clipboard_copy_preview.rb b/previews/primer/beta/clipboard_copy_preview.rb index 2f73b1d992..3295482755 100644 --- a/previews/primer/beta/clipboard_copy_preview.rb +++ b/previews/primer/beta/clipboard_copy_preview.rb @@ -24,7 +24,7 @@ def default(value: "Text to copy", aria_label: "Copy text to the system clipboar # # @param aria_label [String] # @param value [String] - def text(value: "Text to copy", aria_label: "Copy text to the system clipboard") + def text(value: "Text to copy", aria_label: "Click to copy!") render(Primer::Beta::ClipboardCopy.new(value: value, "aria-label": aria_label)) { "Click to copy!" } end @@ -36,4 +36,4 @@ def element(aria_label: "Copy text to the system clipboard") end end end -end +end \ No newline at end of file From d6b8e3894d1a7c80ae9024c758021aa4900dec39 Mon Sep 17 00:00:00 2001 From: Cameron Dutro Date: Thu, 19 Jan 2023 11:45:26 -0800 Subject: [PATCH 24/25] Use code from dotcom --- test/system/test_case.rb | 102 +++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 42 deletions(-) diff --git a/test/system/test_case.rb b/test/system/test_case.rb index 93e4346fde..b8db655f44 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -41,63 +41,75 @@ def visit_preview(preview_name, params = {}) end def format_accessibility_errors(violations) - # rubocop:disable Lint/UnusedBlockArgument - results = violations.flat_map.with_index do |summary, index| + index = 0 + results = violations.map do |summary| summary["nodes"].map do |node| - violations = node["any"] + node["all"] - # rubocop:disable Security/Eval - eval(Erubi::Engine.new(<<~ERB, trim: true).src) - <%= index + 1 %>) <%= summary["id"] %>: <%= summary["description"] %> (<%= summary["impact"] %>) - <%= summary["helpUrl"] %> - - The following <%= violations.size %> <%= violations.size == 1 ? "node violates" : "nodes violate" %> this rule: - <% violations.each do |_violation| %> - Selector: <%= node["target"].join(", ") %> - HTML: <%= node["html"] %> - <%= node["failureSummary"] %> - <% end %> - ERB - # rubocop:enable Security/Eval - end - end - # rubocop:enable Lint/UnusedBlockArgument - - <<~OUTPUT - Found #{violations.size} accessibility violations: - #{results.join} - OUTPUT + index += 1 + %{ + #{index}) #{summary["id"]}: #{summary["description"]} (#{summary["impact"]}) + #{summary["helpUrl"]} + The following #{node["any"].size} node violate this rule: + #{node["any"].map do |_violation| + items = node["failureSummary"].sub("Fix any of the following:", "").split("\n") + %{Selector: #{node["target"].join(', ')} + HTML: #{node["html"]} + Fix any of the following: + #{items.map { |item| "- #{item.strip}" }.join} + } + end.join} + } + end.join + end.join + %{ + Found #{violations.size} accessibility violations: + #{results} + } end - def assert_accessible - axe_exists = page.driver.evaluate_script("!!window.axe") + def assert_accessible(excludes: []) + excludes = Set.new(AXE_RULES_TO_SKIP) + excludes - results = page.driver.evaluate_async_script(<<~JS) + axe_exists = driver.evaluate_async_script <<~JS const callback = arguments[arguments.length - 1]; - #{File.read('node_modules/axe-core/axe.min.js') unless axe_exists} - - const rulesToRun = axe - .getRules(["wcag2a", "wcag21a", "wcag2aa", "wcag2aaa", "wcag21aa", "wcag21aaa"]) - .map(rule => rule.ruleId) - .filter(id => ![#{AXE_RULES_TO_SKIP.map { |id| "'#{id}'" }.join(', ')}].includes(id)) + callback(!!window.axe) + JS + results = driver.evaluate_async_script <<~JS + const callback = arguments[arguments.length - 1]; + #{File.read("node_modules/axe-core/axe.min.js") unless axe_exists} + // Remove cyclic references + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples + const getCircularReplacer = () => { + const seen = new WeakSet(); + return (key, value) => { + if (typeof value === "object" && value !== null) { + if (seen.has(value)) { + return; + } + seen.add(value); + } + return value; + }; + }; + const excludedRulesConfig = {}; + for (const rule of [#{excludes.map { |id| "'#{id}'" }.join(', ')}]) { + excludedRulesConfig[rule] = { enabled: false }; + } const options = { elementRef: true, - resultTypes: ['violations', 'incomplete'], - runOnly: { - type: 'rule', - values: rulesToRun + resultTypes: ['violations'], + rules: { + ...excludedRulesConfig } } - axe.run(document.body, options).then(res => JSON.parse(JSON.stringify(res))).then(callback); + axe.run(document.body, options).then(res => JSON.parse(JSON.stringify(res, getCircularReplacer()))).then(callback); JS - violations = results["violations"] + results["incomplete"].select do |item| - item["impact"] != "serious" && item["impact"] != "critical" - end + violations = results["violations"] message = format_accessibility_errors(violations) - assert violations.empty?, message + assert violations.size == 0, message end # Capybara Overrides to run accessibility checks when UI changes. @@ -112,5 +124,11 @@ def click_button(locator = nil, **kwargs) assert_accessible end + + private + + def driver + page.driver + end end end From be3fafa8859276eae7fce7af18356385c4919a4c Mon Sep 17 00:00:00 2001 From: Cameron Dutro Date: Thu, 19 Jan 2023 11:59:47 -0800 Subject: [PATCH 25/25] Fix rubocop violations --- .../primer/beta/clipboard_copy_preview.rb | 2 +- test/system/test_case.rb | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/previews/primer/beta/clipboard_copy_preview.rb b/previews/primer/beta/clipboard_copy_preview.rb index 3295482755..bbd835a4a6 100644 --- a/previews/primer/beta/clipboard_copy_preview.rb +++ b/previews/primer/beta/clipboard_copy_preview.rb @@ -36,4 +36,4 @@ def element(aria_label: "Copy text to the system clipboard") end end end -end \ No newline at end of file +end diff --git a/test/system/test_case.rb b/test/system/test_case.rb index b8db655f44..1ad73e6d37 100644 --- a/test/system/test_case.rb +++ b/test/system/test_case.rb @@ -46,24 +46,24 @@ def format_accessibility_errors(violations) summary["nodes"].map do |node| index += 1 %{ - #{index}) #{summary["id"]}: #{summary["description"]} (#{summary["impact"]}) - #{summary["helpUrl"]} - The following #{node["any"].size} node violate this rule: - #{node["any"].map do |_violation| - items = node["failureSummary"].sub("Fix any of the following:", "").split("\n") - %{Selector: #{node["target"].join(', ')} - HTML: #{node["html"]} + #{index}) #{summary['id']}: #{summary['description']} (#{summary['impact']}) + #{summary['helpUrl']} + The following #{node['any'].size} node violate this rule: + #{node['any'].map do |_violation| + items = node['failureSummary'].sub('Fix any of the following:', '').split("\n") + %(Selector: #{node['target'].join(', ')} + HTML: #{node['html']} Fix any of the following: #{items.map { |item| "- #{item.strip}" }.join} - } + ) end.join} } end.join end.join - %{ + %( Found #{violations.size} accessibility violations: #{results} - } + ) end def assert_accessible(excludes: []) @@ -76,7 +76,7 @@ def assert_accessible(excludes: []) results = driver.evaluate_async_script <<~JS const callback = arguments[arguments.length - 1]; - #{File.read("node_modules/axe-core/axe.min.js") unless axe_exists} + #{File.read('node_modules/axe-core/axe.min.js') unless axe_exists} // Remove cyclic references // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples const getCircularReplacer = () => { @@ -109,7 +109,7 @@ def assert_accessible(excludes: []) message = format_accessibility_errors(violations) - assert violations.size == 0, message + assert violations.size.zero?, message end # Capybara Overrides to run accessibility checks when UI changes.