There are no results to show just yet. Please check back
soon!
@@ -466,305 +408,226 @@ const TestPlans = ({ testPlanVersions, triggerPageUpdate = () => {} }) => {
);
return (
-
-
-
diff --git a/client/components/TestRun/queries.js b/client/components/TestRun/queries.js
index 8e334021c..43f58277f 100644
--- a/client/components/TestRun/queries.js
+++ b/client/components/TestRun/queries.js
@@ -41,7 +41,6 @@ export const TEST_RUN_PAGE_QUERY = gql`
}
testPlanReport {
id
- status
conflicts {
source {
test {
@@ -99,6 +98,7 @@ export const TEST_RUN_PAGE_QUERY = gql`
testPlanVersion {
id
title
+ phase
gitSha
testPageUrl
testPlan {
@@ -150,7 +150,6 @@ export const TEST_RUN_PAGE_ANON_QUERY = gql`
query TestPlanRunAnonPage($testPlanReportId: ID!) {
testPlanReport(id: $testPlanReportId) {
id
- status
conflicts {
source {
test {
@@ -208,6 +207,7 @@ export const TEST_RUN_PAGE_ANON_QUERY = gql`
testPlanVersion {
id
title
+ phase
gitSha
testPageUrl
testPlan {
@@ -295,7 +295,6 @@ export const FIND_OR_CREATE_TEST_RESULT_MUTATION = gql`
}
testPlanReport {
id
- status
conflicts {
source {
test {
@@ -353,6 +352,7 @@ export const FIND_OR_CREATE_TEST_RESULT_MUTATION = gql`
testPlanVersion {
id
title
+ phase
gitSha
testPageUrl
testPlan {
@@ -444,7 +444,6 @@ export const SAVE_TEST_RESULT_MUTATION = gql`
}
testPlanReport {
id
- status
conflicts {
source {
test {
@@ -502,6 +501,7 @@ export const SAVE_TEST_RESULT_MUTATION = gql`
testPlanVersion {
id
title
+ phase
gitSha
testPageUrl
testPlan {
@@ -593,7 +593,6 @@ export const SUBMIT_TEST_RESULT_MUTATION = gql`
}
testPlanReport {
id
- status
conflicts {
source {
test {
@@ -651,6 +650,7 @@ export const SUBMIT_TEST_RESULT_MUTATION = gql`
testPlanVersion {
id
title
+ phase
gitSha
testPageUrl
testPlan {
diff --git a/client/routes/index.js b/client/routes/index.js
index 0da144954..1b6325980 100644
--- a/client/routes/index.js
+++ b/client/routes/index.js
@@ -5,13 +5,13 @@ import Home from '@components/Home';
import InvalidRequest from '@components/InvalidRequest';
import NotFound from '@components/NotFound';
import { Reports, Report } from '@components/Reports';
-import CandidateTests from '@components/CandidateTests';
+import CandidateReview from '@components/CandidateReview';
import SignupInstructions from '@components/SignupInstructions';
import TestQueue from '@components/TestQueue';
import TestRun from '@components/TestRun';
import UserSettings from '@components/UserSettings';
-import CandidateTestPlanRun from '@components/CandidateTests/CandidateTestPlanRun';
-import TestManagement from '@components/TestManagement';
+import CandidateTestPlanRun from '@components/CandidateReview/CandidateTestPlanRun';
+import DataManagement from 'client/components/DataManagement';
export default () => (
@@ -58,22 +58,14 @@ export default () => (
} />
-
-
- }
- />
-
-
+
}
/>
+ } />
} />
} />
} />
diff --git a/client/stories/ProvideFeedbackModal.stories.jsx b/client/stories/ProvideFeedbackModal.stories.jsx
index be403cfcc..a2ed6ea9f 100644
--- a/client/stories/ProvideFeedbackModal.stories.jsx
+++ b/client/stories/ProvideFeedbackModal.stories.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import ProvideFeedbackModal from '../components/CandidateTests/CandidateModals/ProvideFeedbackModal';
+import ProvideFeedbackModal from '../components/CandidateReview/CandidateModals/ProvideFeedbackModal';
export default {
component: ProvideFeedbackModal,
diff --git a/client/stories/ThankYouModal.stories.jsx b/client/stories/ThankYouModal.stories.jsx
index 3226994ca..1c6828b62 100644
--- a/client/stories/ThankYouModal.stories.jsx
+++ b/client/stories/ThankYouModal.stories.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import ThankYouModal from '../components/CandidateTests/CandidateModals/ThankYouModal/index.jsx';
+import ThankYouModal from '../components/CandidateReview/CandidateModals/ThankYouModal/index.jsx';
export default {
component: ThankYouModal,
diff --git a/client/tests/DataManagement.test.jsx b/client/tests/DataManagement.test.jsx
new file mode 100644
index 000000000..8b90d1235
--- /dev/null
+++ b/client/tests/DataManagement.test.jsx
@@ -0,0 +1,70 @@
+/**
+ * @jest-environment jsdom
+ */
+
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import { InMemoryCache } from '@apollo/client';
+import { MockedProvider } from '@apollo/client/testing';
+import { BrowserRouter } from 'react-router-dom';
+import '@testing-library/jest-dom/extend-expect';
+
+import DataManagement from '../components/DataManagement';
+
+// eslint-disable-next-line jest/no-mocks-import
+import { DATA_MANAGEMENT_PAGE_POPULATED } from './__mocks__/GraphQLMocks';
+
+const setup = (mocks = []) => {
+ return render(
+
+
+
+
+
+ );
+};
+
+describe('Data Management page', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = setup(DATA_MANAGEMENT_PAGE_POPULATED);
+ });
+
+ it('renders loading state on initialization', async () => {
+ const { getByTestId } = wrapper;
+ const element = getByTestId('page-status');
+
+ expect(element).toBeTruthy();
+ expect(element).toHaveTextContent('Loading');
+ });
+
+ it('renders Status Summary component', async () => {
+ // allow page time to load
+ await waitFor(() => new Promise(res => setTimeout(res, 0)));
+
+ const { queryAllByText } = wrapper;
+ const statusSummaryElement = queryAllByText(
+ /Test Plans Status Summary/i
+ );
+ const testPlanElement = queryAllByText(/Test Plan/i);
+ const coveredAtElement = queryAllByText(/Covered AT/i);
+ const overallStatusElement = queryAllByText(/Overall Status/i);
+ const rdElement = queryAllByText(/R&D Version/i);
+ const draftElement = queryAllByText(/Draft Review/i);
+ const candidateElement = queryAllByText(/Candidate Review/i);
+ const recommendedElement = queryAllByText(/Recommended Version/i);
+
+ expect(statusSummaryElement.length).toBeGreaterThanOrEqual(1);
+ expect(testPlanElement.length).toBeGreaterThanOrEqual(1);
+ expect(coveredAtElement.length).toBeGreaterThanOrEqual(1);
+ expect(overallStatusElement.length).toBeGreaterThanOrEqual(1);
+ expect(rdElement.length).toBeGreaterThanOrEqual(1);
+ expect(draftElement.length).toBeGreaterThanOrEqual(1);
+ expect(candidateElement.length).toBeGreaterThanOrEqual(1);
+ expect(recommendedElement.length).toBeGreaterThanOrEqual(1);
+ });
+});
diff --git a/client/tests/TestManagement.test.jsx b/client/tests/TestManagement.test.jsx
deleted file mode 100644
index 45fe5583a..000000000
--- a/client/tests/TestManagement.test.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * @jest-environment jsdom
- */
-
-import React from 'react';
-import { render, waitFor } from '@testing-library/react';
-import { InMemoryCache } from '@apollo/client';
-import { MockedProvider } from '@apollo/client/testing';
-import { BrowserRouter } from 'react-router-dom';
-import '@testing-library/jest-dom/extend-expect';
-
-import TestManagement from '../components/TestManagement';
-
-// eslint-disable-next-line jest/no-mocks-import
-import { TEST_MANAGEMENT_PAGE_POPULATED } from './__mocks__/GraphQLMocks';
-
-const setup = (mocks = []) => {
- return render(
-
-
-
-
-
- );
-};
-
-describe('Test Management page', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = setup(TEST_MANAGEMENT_PAGE_POPULATED);
- });
-
- it('renders loading state on initialization', async () => {
- const { getByTestId } = wrapper;
- const element = getByTestId('page-status');
-
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent('Loading');
- });
-
- it('renders Status Summary component', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryAllByText } = wrapper;
- const statusSummaryElement = queryAllByText(/Status Summary/i);
- const testPlansElement = queryAllByText(/Test Plans/i);
- const phaseElement = queryAllByText(/Phase/i);
- const candidateElements = queryAllByText(/Candidate/i);
- const notTestedElements = queryAllByText(/Not tested/i);
-
- expect(statusSummaryElement.length).toBeGreaterThanOrEqual(1);
- expect(testPlansElement.length).toBeGreaterThanOrEqual(1);
- expect(phaseElement.length).toBeGreaterThanOrEqual(1);
- expect(candidateElements.length).toBeGreaterThanOrEqual(1);
- expect(notTestedElements.length).toBeGreaterThanOrEqual(1);
- });
-});
diff --git a/client/tests/__mocks__/GraphQLMocks.js b/client/tests/__mocks__/GraphQLMocks.js
index 29fe9cff8..d4f7b4b2b 100644
--- a/client/tests/__mocks__/GraphQLMocks.js
+++ b/client/tests/__mocks__/GraphQLMocks.js
@@ -1,5 +1,5 @@
import { TEST_QUEUE_PAGE_QUERY } from '../../components/TestQueue/queries';
-import { TEST_MANAGEMENT_PAGE_QUERY } from '../../components/TestManagement/queries';
+import { DATA_MANAGEMENT_PAGE_QUERY } from '../../components/DataManagement/queries';
export const TEST_QUEUE_PAGE_NOT_POPULATED_MOCK_ADMIN = [
{
@@ -219,11 +219,13 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_ADMIN = [
status: 'DRAFT',
conflictsLength: 0,
runnableTestsLength: 17,
+ markedFinalAt: null,
at: { id: '1', name: 'JAWS' },
browser: { id: '2', name: 'Chrome' },
testPlanVersion: {
id: '1',
title: 'Checkbox Example (Two State)',
+ phase: 'DRAFT',
gitSha: 'b7078039f789c125e269cb8f8632f57a03d4c50b',
gitMessage: 'The message for this SHA',
testPlan: { directory: 'checkbox' },
@@ -245,11 +247,13 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_ADMIN = [
status: 'DRAFT',
conflictsLength: 0,
runnableTestsLength: 17,
+ markedFinalAt: null,
at: { id: '3', name: 'VoiceOver for macOS' },
browser: { id: '3', name: 'Safari' },
testPlanVersion: {
id: '1',
title: 'Checkbox Example (Two State)',
+ phase: 'DRAFT',
gitSha: 'b7078039f789c125e269cb8f8632f57a03d4c50b',
gitMessage: 'The message for this SHA',
testPlan: { directory: 'checkbox' },
@@ -271,11 +275,13 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_ADMIN = [
status: 'DRAFT',
conflictsLength: 3,
runnableTestsLength: 17,
+ markedFinalAt: null,
at: { id: '2', name: 'NVDA' },
browser: { id: '1', name: 'Firefox' },
testPlanVersion: {
id: '1',
title: 'Checkbox Example (Two State)',
+ phase: 'DRAFT',
gitSha: 'b7078039f789c125e269cb8f8632f57a03d4c50b',
gitMessage: 'The message for this SHA',
testPlan: { directory: 'checkbox' },
@@ -449,6 +455,7 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_TESTER = [
status: 'DRAFT',
conflictsLength: 0,
runnableTestsLength: 17,
+ markedFinalAt: null,
at: {
id: '2',
name: 'NVDA'
@@ -460,6 +467,7 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_TESTER = [
testPlanVersion: {
id: '65',
title: 'Checkbox Example (Two State)',
+ phase: 'DRAFT',
gitSha: 'aea64f84b8fa8b21e94f5d9afd7035570bc1bed3',
gitMessage: 'The message for this SHA',
testPlan: {
@@ -491,6 +499,7 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_TESTER = [
status: 'DRAFT',
conflictsLength: 0,
runnableTestsLength: 17,
+ markedFinalAt: null,
at: {
id: '2',
name: 'JAWS'
@@ -502,6 +511,7 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_TESTER = [
testPlanVersion: {
id: '65',
title: 'Checkbox Example (Two State)',
+ phase: 'DRAFT',
gitSha: 'aea64f84b8fa8b21e94f5d9afd7035570bc1bed3',
gitMessage: 'The message for this SHA',
testPlan: {
@@ -525,6 +535,7 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_TESTER = [
status: 'DRAFT',
conflictsLength: 0,
runnableTestsLength: 15,
+ markedFinalAt: null,
at: {
id: '3',
name: 'VoiceOver for macOS'
@@ -536,6 +547,7 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_TESTER = [
testPlanVersion: {
id: '74',
title: 'Editor Menubar Example',
+ phase: 'DRAFT',
gitSha: 'aea64f84b8fa8b21e94f5d9afd7035570bc1bed3',
gitMessage: 'The message for this SHA',
testPlan: {
@@ -552,13 +564,18 @@ export const TEST_QUEUE_PAGE_POPULATED_MOCK_TESTER = [
}
];
-export const TEST_MANAGEMENT_PAGE_POPULATED = [
+export const DATA_MANAGEMENT_PAGE_POPULATED = [
{
request: {
- query: TEST_MANAGEMENT_PAGE_QUERY
+ query: DATA_MANAGEMENT_PAGE_QUERY
},
result: {
data: {
+ me: {
+ id: '1',
+ username: 'foo-bar',
+ roles: ['ADMIN', 'TESTER']
+ },
ats: [
{
id: '1',
@@ -610,876 +627,871 @@ export const TEST_MANAGEMENT_PAGE_POPULATED = [
],
testPlans: [
{
- directory: 'alert',
- id: 'alert',
- title: 'Alert Example',
- latestTestPlanVersion: {
- id: '1',
- title: 'Alert Example'
- }
+ id: '27',
+ directory: 'radiogroup-aria-activedescendant',
+ title: 'Radio Group Example Using aria-activedescendant'
},
{
- directory: 'banner',
- id: 'banner',
- title: 'Banner Landmark',
- latestTestPlanVersion: {
- id: '2',
- title: 'Banner Landmark'
- }
+ id: '28',
+ directory: 'radiogroup-roving-tabindex',
+ title: 'Radio Group Example Using Roving tabindex'
},
{
- directory: 'breadcrumb',
- id: 'breadcrumb',
- title: 'Breadcrumb Example',
- latestTestPlanVersion: {
- id: '3',
- title: 'Breadcrumb Example'
- }
+ id: '31',
+ directory: 'slider-multithumb',
+ title: 'Horizontal Multi-Thumb Slider'
},
{
- directory: 'checkbox',
- id: 'checkbox',
- title: 'Checkbox Example (Two State)',
- latestTestPlanVersion: {
- id: '4',
- title: 'Checkbox Example (Two State)'
- }
+ id: '16',
+ directory: 'link-css',
+ title: 'Link Example 3 (CSS :before content property on a span element)'
},
{
- directory: 'checkbox-tri-state',
- id: 'checkbox-tri-state',
- title: 'Checkbox Example (Mixed-State)',
- latestTestPlanVersion: {
- id: '5',
- title: 'Checkbox Example (Mixed-State)'
- }
+ id: '17',
+ directory: 'link-img-alt',
+ title: 'Link Example 2 (img element with alt attribute)'
},
{
- directory: 'combobox-autocomplete-both-updated',
- id: 'combobox-autocomplete-both-updated',
- title: 'Combobox with Both List and Inline Autocomplete Example',
- latestTestPlanVersion: {
- id: '6',
- title: 'Combobox with Both List and Inline Autocomplete Example'
- }
+ id: '1',
+ directory: 'alert',
+ title: 'Alert Example'
},
{
- directory: 'combobox-select-only',
- id: 'combobox-select-only',
- title: 'Select Only Combobox Example',
- latestTestPlanVersion: {
- id: '7',
- title: 'Select Only Combobox Example'
- }
+ id: '13',
+ directory: 'disclosure-navigation',
+ title: 'Disclosure Navigation Menu Example'
},
{
- directory: 'command-button',
- id: 'command-button',
- title: 'Command Button Example',
- latestTestPlanVersion: {
- id: '8',
- title: 'Command Button Example'
- }
+ id: '5',
+ directory: 'checkbox-tri-state',
+ title: 'Checkbox Example (Mixed-State)'
},
{
- directory: 'complementary',
- id: 'complementary',
- title: 'Complementary Landmark',
- latestTestPlanVersion: {
- id: '9',
- title: 'Complementary Landmark'
- }
+ id: '3',
+ directory: 'breadcrumb',
+ title: 'Breadcrumb Example'
},
{
- directory: 'contentinfo',
- id: 'contentinfo',
- title: 'Contentinfo Landmark',
- latestTestPlanVersion: {
- id: '10',
- title: 'Contentinfo Landmark'
- }
+ id: '19',
+ directory: 'main',
+ title: 'Main Landmark'
},
{
- directory: 'datepicker-spin-button',
- id: 'datepicker-spin-button',
- title: 'Date Picker Spin Button Example',
- latestTestPlanVersion: {
- id: '11',
- title: 'Date Picker Spin Button Example'
- }
+ id: '24',
+ directory: 'meter',
+ title: 'Meter'
},
{
- directory: 'disclosure-faq',
- id: 'disclosure-faq',
- title: 'Disclosure of Answers to Frequently Asked Questions Example',
- latestTestPlanVersion: {
- id: '12',
- title: 'Disclosure of Answers to Frequently Asked Questions Example'
- }
+ id: '32',
+ directory: 'switch',
+ title: 'Switch Example'
},
{
- directory: 'disclosure-navigation',
- id: 'disclosure-navigation',
- title: 'Disclosure Navigation Menu Example',
- latestTestPlanVersion: {
- id: '13',
- title: 'Disclosure Navigation Menu Example'
- }
+ id: '26',
+ directory: 'modal-dialog',
+ title: 'Modal Dialog Example'
},
{
- directory: 'form',
- id: 'form',
- title: 'Form Landmark',
- latestTestPlanVersion: {
- id: '14',
- title: 'Form Landmark'
- }
+ id: '22',
+ directory: 'menu-button-navigation',
+ title: 'Navigation Menu Button'
},
{
- directory: 'horizontal-slider',
- id: 'horizontal-slider',
- title: 'Color Viewer Slider',
- latestTestPlanVersion: {
- id: '15',
- title: 'Color Viewer Slider'
- }
+ id: '34',
+ directory: 'toggle-button',
+ title: 'Toggle Button'
},
{
- directory: 'link-css',
- id: 'link-css',
- title: 'Link Example 3 (CSS :before content property on a span element)',
- latestTestPlanVersion: {
- id: '16',
- title: 'Link Example 3 (CSS :before content property on a span element)'
- }
+ id: '18',
+ directory: 'link-span-text',
+ title: 'Link Example 1 (span element with text content)'
},
{
- directory: 'link-img-alt',
- id: 'link-img-alt',
- title: 'Link Example 2 (img element with alt attribute)',
- latestTestPlanVersion: {
- id: '17',
- title: 'Link Example 2 (img element with alt attribute)'
- }
+ id: '8',
+ directory: 'command-button',
+ title: 'Command Button Example'
},
{
- directory: 'link-span-text',
- id: 'link-span-text',
- title: 'Link Example 1 (span element with text content)',
- latestTestPlanVersion: {
- id: '18',
- title: 'Link Example 1 (span element with text content)'
- }
+ id: '15',
+ directory: 'horizontal-slider',
+ title: 'Color Viewer Slider'
},
{
- directory: 'main',
- id: 'main',
- title: 'Main Landmark',
- latestTestPlanVersion: {
- id: '19',
- title: 'Main Landmark'
- }
+ id: '6',
+ directory: 'combobox-autocomplete-both-updated',
+ title: 'Combobox with Both List and Inline Autocomplete Example'
},
{
- directory: 'menu-button-actions',
- id: 'menu-button-actions',
- title: 'Action Menu Button Example Using element.focus()',
- latestTestPlanVersion: {
- id: '20',
- title: 'Action Menu Button Example Using element.focus()'
- }
+ id: '7',
+ directory: 'combobox-select-only',
+ title: 'Select Only Combobox Example'
},
{
- directory: 'menu-button-actions-active-descendant',
- id: 'menu-button-actions-active-descendant',
- title: 'Action Menu Button Example Using aria-activedescendant',
- latestTestPlanVersion: {
- id: '21',
- title: 'Action Menu Button Example Using aria-activedescendant'
- }
+ id: '4',
+ directory: 'checkbox',
+ title: 'Checkbox Example (Two State)'
},
{
- directory: 'menu-button-navigation',
- id: 'menu-button-navigation',
- title: 'Navigation Menu Button',
- latestTestPlanVersion: {
- id: '22',
- title: 'Navigation Menu Button'
- }
+ id: '9',
+ directory: 'complementary',
+ title: 'Complementary Landmark'
},
{
- directory: 'menubar-editor',
- id: 'menubar-editor',
- title: 'Editor Menubar Example',
- latestTestPlanVersion: {
- id: '23',
- title: 'Editor Menubar Example'
- }
+ id: '10',
+ directory: 'contentinfo',
+ title: 'Contentinfo Landmark'
},
{
- directory: 'meter',
- id: 'meter',
- title: 'Meter',
- latestTestPlanVersion: {
- id: '24',
- title: 'Meter'
- }
+ id: '11',
+ directory: 'datepicker-spin-button',
+ title: 'Date Picker Spin Button Example'
},
{
- directory: 'minimal-data-grid',
- id: 'minimal-data-grid',
- title: 'Data Grid Example 1: Minimal Data Grid',
- latestTestPlanVersion: {
- id: '25',
- title: 'Data Grid Example 1: Minimal Data Grid'
- }
+ id: '12',
+ directory: 'disclosure-faq',
+ title: 'Disclosure of Answers to Frequently Asked Questions Example'
},
{
- directory: 'modal-dialog',
- id: 'modal-dialog',
- title: 'Modal Dialog Example',
- latestTestPlanVersion: {
- id: '26',
- title: 'Modal Dialog Example'
- }
+ id: '14',
+ directory: 'form',
+ title: 'Form Landmark'
},
{
- directory: 'radiogroup-aria-activedescendant',
- id: 'radiogroup-aria-activedescendant',
- title: 'Radio Group Example Using aria-activedescendant',
- latestTestPlanVersion: {
- id: '27',
- title: 'Radio Group Example Using aria-activedescendant'
- }
+ id: '20',
+ directory: 'menu-button-actions',
+ title: 'Action Menu Button Example Using element.focus()'
},
{
- directory: 'radiogroup-roving-tabindex',
- id: 'radiogroup-roving-tabindex',
- title: 'Radio Group Example Using Roving tabindex',
- latestTestPlanVersion: {
- id: '28',
- title: 'Radio Group Example Using Roving tabindex'
- }
+ id: '21',
+ directory: 'menu-button-actions-active-descendant',
+ title: 'Action Menu Button Example Using aria-activedescendant'
},
{
- directory: 'rating-slider',
- id: 'rating-slider',
- title: 'Rating Slider',
- latestTestPlanVersion: {
- id: '29',
- title: 'Rating Slider'
- }
+ id: '23',
+ directory: 'menubar-editor',
+ title: 'Editor Menubar Example'
},
{
- directory: 'seek-slider',
- id: 'seek-slider',
- title: 'Media Seek Slider',
- latestTestPlanVersion: {
- id: '30',
- title: 'Media Seek Slider'
- }
+ id: '25',
+ directory: 'minimal-data-grid',
+ title: 'Data Grid Example 1: Minimal Data Grid'
},
{
- directory: 'slider-multithumb',
- id: 'slider-multithumb',
- title: 'Horizontal Multi-Thumb Slider',
- latestTestPlanVersion: {
- id: '31',
- title: 'Horizontal Multi-Thumb Slider'
- }
+ id: '29',
+ directory: 'rating-slider',
+ title: 'Rating Slider'
},
{
- directory: 'switch',
- id: 'switch',
- title: 'Switch Example',
- latestTestPlanVersion: {
- id: '32',
- title: 'Switch Example'
- }
+ id: '30',
+ directory: 'seek-slider',
+ title: 'Media Seek Slider'
},
{
+ id: '33',
directory: 'tabs-manual-activation',
- id: 'tabs-manual-activation',
- title: 'Tabs with Manual Activation',
- latestTestPlanVersion: {
- id: '33',
- title: 'Tabs with Manual Activation'
- }
+ title: 'Tabs with Manual Activation'
},
{
- directory: 'toggle-button',
- id: 'toggle-button',
- title: 'Toggle Button',
- latestTestPlanVersion: {
- id: '34',
- title: 'Toggle Button'
- }
+ id: '35',
+ directory: 'vertical-temperature-slider',
+ title: 'Vertical Temperature Slider'
},
{
- directory: 'vertical-temperature-slider',
- id: 'vertical-temperature-slider',
- title: 'Vertical Temperature Slider',
- latestTestPlanVersion: {
- id: '35',
- title: 'Vertical Temperature Slider'
- }
+ id: '2',
+ directory: 'banner',
+ title: 'Banner Landmark'
}
],
testPlanVersions: [
{
- id: '21',
- title: 'Action Menu Button Example Using aria-activedescendant',
+ id: '28',
+ title: 'Radio Group Example Using Roving tabindex',
+ phase: 'RD',
gitSha: '1768070bd68beefef29284b568d2da910b449c14',
gitMessage:
'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ updatedAt: '2023-04-10T18:22:22.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'menu-button-actions-active-descendant'
+ directory: 'radiogroup-roving-tabindex'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '20',
- title: 'Action Menu Button Example Using element.focus()',
+ id: '27',
+ title: 'Radio Group Example Using aria-activedescendant',
+ phase: 'RD',
gitSha: '1768070bd68beefef29284b568d2da910b449c14',
gitMessage:
'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ updatedAt: '2023-04-10T18:22:22.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'menu-button-actions'
+ directory: 'radiogroup-aria-activedescendant'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '1',
- title: 'Alert Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '31',
+ title: 'Horizontal Multi-Thumb Slider',
+ phase: 'RD',
+ gitSha: 'b5fe3efd569518e449ef9a0978b0dec1f2a08bd6',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Create tests for APG design pattern example: Horizontal Multi-Thumb Slider (#511)',
+ updatedAt: '2023-03-20T21:24:41.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'alert'
+ directory: 'slider-multithumb'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '2',
- title: 'Banner Landmark',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '16',
+ title: 'Link Example 3 (CSS :before content property on a span element)',
+ phase: 'RD',
+ gitSha: '7a8454bca6de980199868101431817cea03cce35',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Create tests for APG design pattern example: Link Example 3 (CSS :before content property on a span element) (#518)',
+ updatedAt: '2023-03-13T22:10:13.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'banner'
+ directory: 'link-css'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '3',
- title: 'Breadcrumb Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '17',
+ title: 'Link Example 2 (img element with alt attribute)',
+ phase: 'RD',
+ gitSha: 'dc637636cff74b51f5c468ff3b81bd1f38aefbb2',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Create tests for APG design pattern example: Link Example 2 (img element with alt attribute) (#516)',
+ updatedAt: '2023-03-13T19:51:48.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'breadcrumb'
+ directory: 'link-img-alt'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '5',
- title: 'Checkbox Example (Mixed-State)',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '1',
+ title: 'Alert Example',
+ phase: 'DRAFT',
+ gitSha: '0928bcf530efcf4faa677285439701537674e014',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Alert and Radiogroup/activedescendent updates (#865)',
+ updatedAt: '2022-12-08T21:47:42.000Z',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'checkbox-tri-state'
+ directory: 'alert'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: [
+ {
+ id: '7',
+ markedFinalAt: null,
+ at: {
+ id: '3',
+ name: 'VoiceOver for macOS'
+ },
+ browser: {
+ id: '1',
+ name: 'Firefox'
+ },
+ issues: []
+ }
+ ]
},
{
- id: '4',
- title: 'Checkbox Example (Two State)',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '13',
+ title: 'Disclosure Navigation Menu Example',
+ phase: 'RD',
+ gitSha: '179ba0f438aaa5781b3ec8a4033d6bf9f757360b',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Delete up arrow command for VoiceOver when navigating backwards to a disclosure button (#845)',
+ updatedAt: '2022-10-31T19:29:17.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'checkbox'
+ directory: 'disclosure-navigation'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '15',
- title: 'Color Viewer Slider',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '3',
+ title: 'Breadcrumb Example',
+ phase: 'RD',
+ gitSha: '1aa3b74d24d340362e9f511eae33788d55487d12',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Add down arrow command to the Navigate forwards out of the Breadcrumb navigation landmark task for JAWS (#803)',
+ updatedAt: '2022-08-10T18:44:16.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'horizontal-slider'
+ directory: 'breadcrumb'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '6',
- title: 'Combobox with Both List and Inline Autocomplete Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '19',
+ title: 'Main Landmark',
+ phase: 'RD',
+ gitSha: 'c87a66ea13a2b6fac6d79fe1fb0b7a2f721dcd22',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Create updated tests for APG design pattern example: Main landmark (#707)',
+ updatedAt: '2022-08-05T17:46:37.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'combobox-autocomplete-both-updated'
+ directory: 'main'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '8',
- title: 'Command Button Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '24',
+ title: 'Meter',
+ phase: 'RD',
+ gitSha: '32d2d9db48becfc008fc566b569ac1563576ceb9',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Create updated tests for APG design pattern example: Meter (#692)',
+ updatedAt: '2022-08-05T17:02:59.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'command-button'
+ directory: 'meter'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '9',
- title: 'Complementary Landmark',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '32',
+ title: 'Switch Example',
+ phase: 'RD',
+ gitSha: '9d0e4e3d1040d64d9db69647e615c4ec0be723c2',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Create updated tests for APG design pattern example: Switch (#691)',
+ updatedAt: '2022-08-05T16:13:44.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'complementary'
+ directory: 'switch'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '10',
- title: 'Contentinfo Landmark',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '22',
+ title: 'Navigation Menu Button',
+ phase: 'RD',
+ gitSha: 'ecf05f484292189789f4db8b1ec41b19db38e567',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Tasks 4, 5 and 6: corrected link name "Navigate backwards from here" (#734)',
+ updatedAt: '2022-05-26T16:14:17.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'contentinfo'
+ directory: 'menu-button-navigation'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '25',
- title: 'Data Grid Example 1: Minimal Data Grid',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '34',
+ title: 'Toggle Button',
+ phase: 'DRAFT',
+ gitSha: '022340081280b8cafb8ae0716a5b67e9ab942ef4',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Delete duplicated assertion for operating a not pressed togle button (VoiceOver) (#716)',
+ updatedAt: '2022-05-18T20:51:40.000Z',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'minimal-data-grid'
+ directory: 'toggle-button'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
- },
- {
- id: '11',
- title: 'Date Picker Spin Button Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ testPlanReports: [
+ {
+ id: '1',
+ markedFinalAt: null,
+ at: {
+ id: '1',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '2',
+ name: 'Chrome'
+ },
+ issues: []
+ }
+ ]
+ },
+ {
+ id: '8',
+ title: 'Command Button Example',
+ phase: 'RD',
+ gitSha: '0c466eec96c8cafc9961232c85e14758c4589525',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Fix navigation link positions in three test plans: link, command button and toggle button (#709)',
+ updatedAt: '2022-05-04T21:33:31.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'datepicker-spin-button'
+ directory: 'command-button'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '13',
- title: 'Disclosure Navigation Menu Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '18',
+ title: 'Link Example 1 (span element with text content)',
+ phase: 'RD',
+ gitSha: '0c466eec96c8cafc9961232c85e14758c4589525',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Fix navigation link positions in three test plans: link, command button and toggle button (#709)',
+ updatedAt: '2022-05-04T21:33:31.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'disclosure-navigation'
+ directory: 'link-span-text'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '12',
- title: 'Disclosure of Answers to Frequently Asked Questions Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '15',
+ title: 'Color Viewer Slider',
+ phase: 'RD',
+ gitSha: '1c6ef2fbef5fc056c622c802bebedaa14f2c8d40',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Create updated tests for APG design pattern example: Color Viewer Slider (#686)',
+ updatedAt: '2022-04-14T18:06:40.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'disclosure-faq'
+ directory: 'horizontal-slider'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '23',
- title: 'Editor Menubar Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ id: '6',
+ title: 'Combobox with Both List and Inline Autocomplete Example',
+ phase: 'RD',
+ gitSha: '6b2cbcbdbd5f6867cd3c9e96362817c353335187',
+ gitMessage: "typo: double word 'the' (#595)",
+ updatedAt: '2022-03-29T16:02:56.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'menubar-editor'
+ directory: 'combobox-autocomplete-both-updated'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '14',
- title: 'Form Landmark',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '21',
+ title: 'Action Menu Button Example Using aria-activedescendant',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'form'
+ directory: 'menu-button-actions-active-descendant'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '31',
- title: 'Horizontal Multi-Thumb Slider',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '20',
+ title: 'Action Menu Button Example Using element.focus()',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'slider-multithumb'
+ directory: 'menu-button-actions'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '18',
- title: 'Link Example 1 (span element with text content)',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '2',
+ title: 'Banner Landmark',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'link-span-text'
+ directory: 'banner'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '17',
- title: 'Link Example 2 (img element with alt attribute)',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '4',
+ title: 'Checkbox Example (Two State)',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'link-img-alt'
+ directory: 'checkbox'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '16',
- title: 'Link Example 3 (CSS :before content property on a span element)',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '9',
+ title: 'Complementary Landmark',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'link-css'
+ directory: 'complementary'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '19',
- title: 'Main Landmark',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '10',
+ title: 'Contentinfo Landmark',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'main'
+ directory: 'contentinfo'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '30',
- title: 'Media Seek Slider',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '25',
+ title: 'Data Grid Example 1: Minimal Data Grid',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'seek-slider'
+ directory: 'minimal-data-grid'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '24',
- title: 'Meter',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '11',
+ title: 'Date Picker Spin Button Example',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'meter'
+ directory: 'datepicker-spin-button'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '26',
- title: 'Modal Dialog Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '12',
+ title: 'Disclosure of Answers to Frequently Asked Questions Example',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'modal-dialog'
+ directory: 'disclosure-faq'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '22',
- title: 'Navigation Menu Button',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '23',
+ title: 'Editor Menubar Example',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'menu-button-navigation'
+ directory: 'menubar-editor'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '28',
- title: 'Radio Group Example Using Roving tabindex',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '14',
+ title: 'Form Landmark',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'radiogroup-roving-tabindex'
+ directory: 'form'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
- id: '27',
- title: 'Radio Group Example Using aria-activedescendant',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ id: '30',
+ title: 'Media Seek Slider',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
- directory: 'radiogroup-aria-activedescendant'
+ directory: 'seek-slider'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
id: '29',
title: 'Rating Slider',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
directory: 'rating-slider'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
- },
- {
- id: '7',
- title: 'Select Only Combobox Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'combobox-select-only'
- },
- updatedAt: '2023-04-10T18:22:22.000Z'
- },
- {
- id: '32',
- title: 'Switch Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'switch'
- },
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
id: '33',
title: 'Tabs with Manual Activation',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
directory: 'tabs-manual-activation'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
- },
- {
- id: '34',
- title: 'Toggle Button',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'toggle-button'
- },
- updatedAt: '2023-04-10T18:22:22.000Z'
+ testPlanReports: []
},
{
id: '35',
title: 'Vertical Temperature Slider',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
testPlan: {
directory: 'vertical-temperature-slider'
},
- updatedAt: '2023-04-10T18:22:22.000Z'
- }
- ],
- testPlanReports: [
- {
- id: '2',
- status: 'DRAFT',
- at: {
- id: '2',
- name: 'NVDA'
- },
- latestAtVersionReleasedAt: {
- id: '2',
- name: '2020.4',
- releasedAt: '2021-02-19T05:00:00.000Z'
- },
- browser: {
- id: '1',
- name: 'Firefox'
- },
- testPlanVersion: {
- id: '7',
- title: 'Select Only Combobox Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'combobox-select-only'
- },
- updatedAt: '2023-04-10T18:22:22.000Z'
- }
- },
- {
- id: '4',
- status: 'CANDIDATE',
- at: {
- id: '2',
- name: 'NVDA'
- },
- latestAtVersionReleasedAt: {
- id: '2',
- name: '2020.4',
- releasedAt: '2021-02-19T05:00:00.000Z'
- },
- browser: {
- id: '1',
- name: 'Firefox'
- },
- testPlanVersion: {
- id: '26',
- title: 'Modal Dialog Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'modal-dialog'
- },
- updatedAt: '2023-04-10T18:22:22.000Z'
- }
+ testPlanReports: []
},
{
- id: '3',
- status: 'CANDIDATE',
- at: {
- id: '1',
- name: 'JAWS'
- },
- latestAtVersionReleasedAt: {
- id: '1',
- name: '2021.2111.13',
- releasedAt: '2021-11-01T04:00:00.000Z'
- },
- browser: {
- id: '2',
- name: 'Chrome'
+ id: '7',
+ title: 'Select Only Combobox Example',
+ phase: 'CANDIDATE',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: '2023-08-03T20:22:51.263Z',
+ recommendedPhaseTargetDate: '2024-01-30T21:22:51.263Z',
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'combobox-select-only'
},
- testPlanVersion: {
- id: '26',
- title: 'Modal Dialog Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'modal-dialog'
- },
- updatedAt: '2023-04-10T18:22:22.000Z'
- }
+ testPlanReports: [
+ {
+ id: '2',
+ markedFinalAt: '2023-08-03T20:20:48.535Z',
+ at: {
+ id: '2',
+ name: 'NVDA'
+ },
+ browser: {
+ id: '1',
+ name: 'Firefox'
+ },
+ issues: []
+ }
+ ]
},
{
- id: '1',
- status: 'DRAFT',
- at: {
- id: '1',
- name: 'JAWS'
- },
- latestAtVersionReleasedAt: {
- id: '1',
- name: '2021.2111.13',
- releasedAt: '2021-11-01T04:00:00.000Z'
- },
- browser: {
- id: '2',
- name: 'Chrome'
+ id: '5',
+ title: 'Checkbox Example (Mixed-State)',
+ phase: 'RECOMMENDED',
+ gitSha: 'b3d0576a2901ea7f100f49a994b64edbecf81cff',
+ gitMessage:
+ 'Modify VoiceOver commands for task 7 (#842)',
+ updatedAt: '2022-10-24T21:33:12.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ recommendedPhaseTargetDate: '2023-01-02T00:00:00.000Z',
+ recommendedPhaseReachedAt: '2023-01-03T00:00:00.000Z',
+ testPlan: {
+ directory: 'checkbox-tri-state'
},
- testPlanVersion: {
- id: '34',
- title: 'Toggle Button',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'toggle-button'
- },
- updatedAt: '2023-04-10T18:22:22.000Z'
- }
+ testPlanReports: [
+ {
+ id: '6',
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ at: {
+ id: '3',
+ name: 'VoiceOver for macOS'
+ },
+ browser: {
+ id: '3',
+ name: 'Safari'
+ },
+ issues: []
+ }
+ ]
},
{
- id: '6',
- status: 'CANDIDATE',
- at: {
- id: '3',
- name: 'VoiceOver for macOS'
- },
- latestAtVersionReleasedAt: {
- id: '3',
- name: '11.6 (20G165)',
- releasedAt: '2019-09-01T04:00:00.000Z'
- },
- browser: {
- id: '3',
- name: 'Safari'
+ id: '26',
+ title: 'Modal Dialog Example',
+ phase: 'CANDIDATE',
+ gitSha: 'd0e16b42179de6f6c070da2310e99de837c71215',
+ gitMessage:
+ 'Delete down arrow command for navigating to the beginning of a dialog with JAWS and add the ESC command to exit forms or focus mode (#759)',
+ updatedAt: '2022-06-22T17:56:16.000Z',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ recommendedPhaseTargetDate: '2023-01-02T00:00:00.000Z',
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'modal-dialog'
},
- testPlanVersion: {
- id: '5',
- title: 'Checkbox Example (Mixed-State)',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'checkbox-tri-state'
+ testPlanReports: [
+ {
+ id: '5',
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ at: {
+ id: '3',
+ name: 'VoiceOver for macOS'
+ },
+ browser: {
+ id: '3',
+ name: 'Safari'
+ },
+ issues: []
},
- updatedAt: '2023-04-10T18:22:22.000Z'
- }
- },
- {
- id: '5',
- status: 'CANDIDATE',
- at: {
- id: '3',
- name: 'VoiceOver for macOS'
- },
- latestAtVersionReleasedAt: {
- id: '3',
- name: '11.6 (20G165)',
- releasedAt: '2019-09-01T04:00:00.000Z'
- },
- browser: {
- id: '3',
- name: 'Safari'
- },
- testPlanVersion: {
- id: '26',
- title: 'Modal Dialog Example',
- gitSha: '1768070bd68beefef29284b568d2da910b449c14',
- gitMessage:
- 'Remove Tab and Shift+Tab from radiogroup tests when navigating out of the start and end of a radio group (reading mode and VoiceOver only) (#928)',
- testPlan: {
- directory: 'modal-dialog'
+ {
+ id: '4',
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ at: {
+ id: '2',
+ name: 'NVDA'
+ },
+ browser: {
+ id: '1',
+ name: 'Firefox'
+ },
+ issues: []
},
- updatedAt: '2023-04-10T18:22:22.000Z'
- }
+ {
+ id: '3',
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ at: {
+ id: '1',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '2',
+ name: 'Chrome'
+ },
+ issues: []
+ }
+ ]
}
]
}
diff --git a/client/utils/aria.js b/client/utils/aria.js
index eaf6450de..944ef376f 100644
--- a/client/utils/aria.js
+++ b/client/utils/aria.js
@@ -8,3 +8,16 @@ export const buildTestPageUrl = (gitSha, directory, testReferencePath) => {
const BASE_PATH = '/aria-at';
return `${BASE_PATH}/${gitSha}/build/tests/${directory}/${testReferencePath}`;
};
+
+export const derivePhaseName = name => {
+ switch (name) {
+ case 'RD':
+ return 'R&D';
+ case 'DRAFT':
+ return 'Draft';
+ case 'CANDIDATE':
+ return 'Candidate';
+ case 'RECOMMENDED':
+ return 'Recommended';
+ }
+};
diff --git a/client/utils/formatter.js b/client/utils/formatter.js
index e53d59fd8..6be2f3c2d 100644
--- a/client/utils/formatter.js
+++ b/client/utils/formatter.js
@@ -42,3 +42,10 @@ export const convertStringFormatToAnotherFormat = (
export const isValidDate = (date, format = 'DD-MM-YYYY') => {
return moment(date, format).isValid();
};
+
+export const checkTimeBetweenDates = (date, otherDate, unit = 'days') => {
+ const _date = moment(date);
+ const _otherDate = moment(otherDate);
+
+ return _date.diff(_otherDate, unit);
+};
diff --git a/client/webpack.config.js b/client/webpack.config.js
index b3b8fc590..68a8815e7 100644
--- a/client/webpack.config.js
+++ b/client/webpack.config.js
@@ -70,7 +70,7 @@ module.exports = {
hot: 'only',
proxy: [
{
- context: ['/aria-at', '/api', '/embed'],
+ context: ['/aria-at', '/api', '/embed', '/test-review'],
target: 'http://localhost:8000'
}
]
diff --git a/docs/database.md b/docs/database.md
index 2804e1e00..0be2c999b 100644
--- a/docs/database.md
+++ b/docs/database.md
@@ -73,6 +73,11 @@ The prerequisite for the following steps is SSH access to the production server.
```
psql -d aria_at_report -f _dump_.sql
```
+5. Run the migrations and seeders:
+ ```
+ yarn sequelize db:migrate;
+ yarn sequelize db:seed:all;
+ ```
## Test Database
diff --git a/server/app.js b/server/app.js
index 719a74662..7c4976bf0 100644
--- a/server/app.js
+++ b/server/app.js
@@ -4,6 +4,7 @@ const cacheMiddleware = require('apicache').middleware;
const proxyMiddleware = require('rawgit/lib/middleware');
const { session } = require('./middleware/session');
const embedApp = require('./apps/embed');
+const testReviewApp = require('./apps/test-review');
const authRoutes = require('./routes/auth');
const testRoutes = require('./routes/tests');
const path = require('path');
@@ -21,7 +22,10 @@ apolloServer.start().then(() => {
});
const listener = express();
-listener.use('/api', app).use('/embed', embedApp);
+listener
+ .use('/api', app)
+ .use('/embed', embedApp)
+ .use('/test-review', testReviewApp);
const baseUrl = 'https://raw.githubusercontent.com';
const onlyStatus200 = (req, res) => res.statusCode === 200;
diff --git a/server/apps/embed.js b/server/apps/embed.js
index 00091aff5..4531b58d7 100644
--- a/server/apps/embed.js
+++ b/server/apps/embed.js
@@ -9,8 +9,8 @@ const hash = require('object-hash');
const app = express();
const handlebarsPath =
process.env.ENVIRONMENT === 'dev' || process.env.ENVIRONMENT === 'test'
- ? 'handlebars'
- : 'server/handlebars';
+ ? 'handlebars/embed'
+ : 'server/handlebars/embed';
// handlebars
const hbs = create({
@@ -44,10 +44,11 @@ const queryReports = async () => {
name
}
}
- testPlanReports(statuses: [CANDIDATE, RECOMMENDED]) {
+ testPlanReports(
+ testPlanVersionPhases: [CANDIDATE, RECOMMENDED]
+ ) {
id
metrics
- status
at {
id
name
@@ -64,6 +65,7 @@ const queryReports = async () => {
testPlanVersion {
id
title
+ phase
updatedAt
testPlan {
id
@@ -185,16 +187,16 @@ const getLatestReportsForPattern = ({ allTestPlanReports, pattern }) => {
});
const hasAnyCandidateReports = Object.values(reportsByAt).find(atReports =>
- atReports.find(report => report.status === 'CANDIDATE')
+ atReports.some(report => report.testPlanVersion.phase === 'CANDIDATE')
);
- let status = hasAnyCandidateReports ? 'CANDIDATE' : 'RECOMMENDED';
+ let phase = hasAnyCandidateReports ? 'CANDIDATE' : 'RECOMMENDED';
return {
title,
allBrowsers,
allAtVersionsByAt,
testPlanVersionIds,
- status,
+ phase,
reportsByAt
};
};
@@ -212,7 +214,7 @@ const renderEmbed = ({
allBrowsers,
allAtVersionsByAt,
testPlanVersionIds,
- status,
+ phase,
reportsByAt
} = getLatestReportsForPattern({ pattern, allTestPlanReports });
const allAtBrowserCombinations = Object.fromEntries(
@@ -232,7 +234,7 @@ const renderEmbed = ({
allAtBrowserCombinations,
title: queryTitle || title || 'Pattern Not Found',
pattern,
- status,
+ phase,
allBrowsers,
allAtVersionsByAt,
reportsByAt,
diff --git a/server/apps/test-review.js b/server/apps/test-review.js
new file mode 100644
index 000000000..f6f59c442
--- /dev/null
+++ b/server/apps/test-review.js
@@ -0,0 +1,372 @@
+const express = require('express');
+const fs = require('fs');
+const fetch = require('node-fetch');
+const { resolve } = require('path');
+const moment = require('moment');
+const { create } = require('express-handlebars');
+const CommandsAPI = require('../handlebars/test-review/scripts/at-commands');
+const {
+ parseCommandCSVRow
+} = require('../handlebars/test-review/scripts/parse-command-csv-row');
+const {
+ createCommandTuplesATModeTaskLookup
+} = require('../handlebars/test-review/scripts/command-tuples-at-mode-task-lookup');
+
+const app = express();
+
+// handlebars setup
+const handlebarsPath =
+ process.env.ENVIRONMENT === 'dev' || process.env.ENVIRONMENT === 'test'
+ ? 'handlebars/test-review'
+ : 'server/handlebars/test-review';
+
+const hbs = create({
+ layoutsDir: resolve(handlebarsPath, 'views/layouts'),
+ extname: 'hbs',
+ defaultLayout: 'index',
+ helpers: require(resolve(handlebarsPath, 'helpers'))
+});
+app.engine('hbs', hbs.engine);
+app.set('view engine', 'hbs');
+app.set('views', resolve(handlebarsPath, 'views'));
+
+// Prepare applicable format for ats.json
+const atsJSONFilePath =
+ process.env.ENVIRONMENT === 'dev' || process.env.ENVIRONMENT === 'test'
+ ? 'resources/ats.json'
+ : 'server/resources/ats.json';
+
+const ats = JSON.parse(fs.readFileSync(atsJSONFilePath, 'utf-8'));
+for (const at of ats) {
+ if (at.name.includes('VoiceOver')) at.key = 'voiceover_macos';
+ else at.key = at.name.toLowerCase();
+}
+
+const getCommitInfo = async commit => {
+ const OWNER = 'w3c';
+ const REPO = 'aria-at';
+
+ let commitDate;
+ let commitMessage;
+
+ try {
+ const response = await fetch(
+ `https://api.github.com/repos/${OWNER}/${REPO}/commits/${commit}`
+ );
+ const data = await response.json();
+
+ if (data.commit) {
+ commitDate = data.commit.author.date;
+ commitMessage = data.commit.message;
+ }
+ } catch (error) {
+ console.error('Error:', error.message);
+ }
+
+ return { commitDate, commitMessage };
+};
+
+const csvToJSON = csvString => {
+ const lines = csvString.trim().split('\n');
+ const headers = lines.shift().split(',');
+ const result = [];
+
+ lines.forEach(line => {
+ const values = line.split(',');
+ const obj = {};
+ let valueIndex = 0;
+
+ headers.forEach(header => {
+ let value = values[valueIndex].trim();
+
+ if (value.startsWith('"') && !value.endsWith('"')) {
+ // If the value starts with a double quote but does not end with it,
+ // continue to concatenate values until the ending quote is found
+ while (
+ valueIndex < values.length &&
+ !values[valueIndex].endsWith('"')
+ ) {
+ valueIndex++;
+ value += `,${values[valueIndex].trim()}`;
+ }
+ }
+
+ if (value.startsWith('"') && value.endsWith('"')) {
+ // If the value is enclosed in double quotes, remove the quotes
+ value = value.slice(1, -1);
+ }
+
+ obj[header] = value;
+ valueIndex++;
+ });
+
+ result.push(obj);
+ });
+
+ return result;
+};
+
+const convertTextToHTML = text => {
+ const blocks = text.split('\n\n');
+ let html = '';
+
+ for (let block of blocks) {
+ const lines = block.split('\n');
+ if (lines[0].startsWith('*')) {
+ html += '
';
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i].trim();
+ if (line.startsWith('*')) {
+ html += '
' + line.substring(1).trim() + '
';
+ }
+ }
+ html += '
';
+ } else {
+ html += '
' + lines.join(' ') + '
';
+ }
+ }
+
+ return html;
+};
+
+const generateTests = async (pattern, commit, commitDate) => {
+ const commandsCSVData = await fetch(
+ `https://raw.githubusercontent.com/w3c/aria-at/${commit}/tests/${pattern}/data/commands.csv`
+ );
+ const commandsCSVText = await commandsCSVData.text();
+ const commandsJSON = csvToJSON(commandsCSVText);
+
+ const testsCSVData = await fetch(
+ `https://raw.githubusercontent.com/w3c/aria-at/${commit}/tests/${pattern}/data/tests.csv`
+ );
+ const testsCSVText = await testsCSVData.text();
+ const testsJSON = csvToJSON(testsCSVText);
+
+ const referencesCSVData = await fetch(
+ `https://raw.githubusercontent.com/w3c/aria-at/${commit}/tests/${pattern}/data/references.csv`
+ );
+ const referencesCSVText = await referencesCSVData.text();
+ const referencesJSON = csvToJSON(referencesCSVText);
+
+ const commandsParsed = commandsJSON.map(parseCommandCSVRow);
+ const commands = createCommandTuplesATModeTaskLookup(commandsParsed);
+ const commandsAPI = new CommandsAPI(commands, ats);
+
+ const tests = [];
+ const scripts = [];
+
+ let scriptNames = [];
+ testsJSON.forEach(testJSON => scriptNames.push(testJSON.setupScript));
+
+ scriptNames = scriptNames.filter(
+ (item, index) => scriptNames.indexOf(item) === index
+ );
+
+ for (const scriptName of scriptNames) {
+ let script = '';
+ try {
+ const scriptData = await fetch(
+ `https://raw.githubusercontent.com/w3c/aria-at/${commit}/tests/${pattern}/data/js/${scriptName}.js`
+ );
+ const scriptText = await scriptData.text();
+
+ const lines = scriptText.split(/\r?\n/);
+ lines.forEach(line => {
+ if (line.trim().length) script += '\t' + line.trim() + '\n';
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ scripts.push(
+ `\t${scriptName}: function(testPageDocument){\n${script}}`
+ );
+ }
+
+ const allATKeys = ats.map(({ key }) => key);
+ const validAppliesTo = ['Screen Readers', 'Desktop Screen Readers'].concat(
+ allATKeys
+ );
+
+ const getAppliesToValues = values => {
+ const checkValue = value => {
+ let v1 = value.trim().toLowerCase();
+ for (let i = 0; i < validAppliesTo.length; i++) {
+ let v2 = validAppliesTo[i];
+ if (v1 === v2.toLowerCase()) {
+ return v2;
+ }
+ }
+ return false;
+ };
+
+ // check for individual assistive technologies
+ const items = values.split(',');
+ const newValues = [];
+ items.filter(item => {
+ const value = checkValue(item);
+ if (value) newValues.push(value);
+ });
+
+ return newValues;
+ };
+
+ const getFormattedAssertion = assertion => {
+ let level = '1';
+ let str = assertion;
+ assertion = assertion.trim();
+
+ // matches a 'colon' when preceded by either of the digits 1 OR 2 (SINGLE CHARACTER), at the start of the string
+ let parts = assertion.split(/(?<=^[1-2]):/g);
+
+ if (parts.length === 2) {
+ level = parts[0];
+ str = parts[1].substring(0);
+ if (level !== '1' && level !== '2') level = '2';
+ }
+
+ if (assertion.length) return [level, str];
+ };
+
+ const getPriorityString = function (priority) {
+ priority = parseInt(priority);
+ if (priority === 1) return 'required';
+ else if (priority === 2) return 'optional';
+ return '';
+ };
+
+ // https://github.com/w3c/aria-at/commit/9d73d6bb274b3fe75b9a8825e020c0546a33a162
+ // This is the date of the last commit before the build folder removal.
+ // Meant to support backward compatability until the existing tests can
+ // be updated to the current structure
+ const buildRemovalDate = new Date('2022-03-10 18:08:36.000000 +00:00');
+ const useBuildInReferencePath =
+ new Date(commitDate).getTime() <= buildRemovalDate.getTime();
+
+ for (const testJSON of testsJSON) {
+ const {
+ task,
+ mode,
+ instructions,
+ setupScript,
+ setupScriptDescription
+ } = testJSON;
+
+ const atTests = [];
+
+ const appliesTo = getAppliesToValues(testJSON.appliesTo);
+ const allRelevantAts =
+ appliesTo[0].toLowerCase() === 'desktop screen readers' ||
+ appliesTo[0].toLowerCase() === 'screen readers'
+ ? allATKeys
+ : appliesTo;
+
+ const assertions = [];
+
+ for (const key of Object.keys(testJSON)) {
+ if (key.includes('assertion')) {
+ if (testJSON[key])
+ assertions.push(getFormattedAssertion(testJSON[key]));
+ }
+ }
+
+ for (const atKey of allRelevantAts.map(a => a.toLowerCase())) {
+ let commands;
+ let at = commandsAPI.isKnownAT(atKey);
+
+ try {
+ commands = commandsAPI.getATCommands(mode, task, at);
+ } catch (error) {
+ // An error will occur if there is no data for a screen reader, ignore it
+ // console.error('commandsAPI.getATCommands.error', error);
+ }
+
+ atTests.push({
+ atName: at.name,
+ atKey: at.key,
+ commands: commands && commands.length ? commands : undefined,
+ assertions:
+ assertions && assertions.length
+ ? assertions.map(a => ({
+ priority: getPriorityString(a[0]),
+ description: a[1]
+ }))
+ : undefined,
+ userInstruction: instructions,
+ modeInstruction: commandsAPI.getModeInstructions(mode, at),
+ setupScriptDescription: setupScriptDescription
+ });
+ }
+
+ let helpLinks = [];
+ const getRef = refId => referencesJSON.find(ref => ref.refId === refId);
+
+ helpLinks.push({
+ link: getRef('example').value,
+ text: `APG example: ${pattern}.html`
+ });
+
+ for (const ref of testJSON.refs.split(' ')) {
+ if (ref)
+ helpLinks.push({
+ link: getRef(ref).value,
+ text: `ARIA specification: ${ref}`
+ });
+ }
+
+ const referenceValue = getRef('reference').value;
+
+ const reference = `/aria-at/${commit}/${
+ useBuildInReferencePath ? 'build/tests/' : 'tests/'
+ }${pattern}/${
+ setupScript
+ ? referenceValue.replace(/\.html$/, `.${setupScript}.html`)
+ : referenceValue
+ }`;
+
+ tests.push({
+ testNumber: tests.length + 1,
+ name: testJSON.title,
+ setupScriptName: setupScript,
+ allRelevantAts,
+ reference,
+ task,
+ mode,
+ atTests,
+ helpLinks
+ });
+ }
+
+ return { tests, scripts };
+};
+
+app.get('/:commit/:pattern', async (req, res) => {
+ const commit = req.params.commit;
+ const pattern = req.params.pattern;
+
+ const { commitDate, commitMessage } = await getCommitInfo(commit);
+ const { tests, scripts: setupScripts } = await generateTests(
+ pattern,
+ commit,
+ commitDate
+ );
+
+ const rendered = await hbs.renderView(
+ resolve(handlebarsPath, 'views/main.hbs'),
+ {
+ ats,
+ tests,
+ pattern,
+ setupScripts,
+ commitMessage: convertTextToHTML(commitMessage),
+ commitDate: moment(commitDate).format('YYYY.MM.DD')
+ }
+ );
+
+ // Disable browser-based caching which could potentially make the embed
+ // contents appear stale even after being refreshed
+ res.set('cache-control', 'must-revalidate').send(rendered);
+});
+
+app.use(express.static(resolve(`${handlebarsPath}/public`)));
+
+module.exports = app;
diff --git a/server/graphql-schema.js b/server/graphql-schema.js
index 1de34c296..d5dcf1779 100644
--- a/server/graphql-schema.js
+++ b/server/graphql-schema.js
@@ -87,6 +87,14 @@ const graphqlSchema = gql`
The Ats which can be run with the specific browser, for example, Jaws can be run with Chrome but not Safari, and Safari works with VoiceOver only.
"""
ats: [At]!
+ """
+ The Ats which are required to move a TestPlanVersion to CANDIDATE phase.
+ """
+ candidateAts: [At]!
+ """
+ The Ats which are required to move a TestPlanVersion to RECOMMENDED phase.
+ """
+ recommendedAts: [At]!
}
"""
@@ -153,6 +161,14 @@ const graphqlSchema = gql`
The browsers which can run the At, for example, Safari can run VoiceOver but not Jaws because Jaws is Windows only.
"""
browsers: [Browser]!
+ """
+ The browsers which are required to move a TestPlanVersion to CANDIDATE phase.
+ """
+ candidateBrowsers: [Browser]!
+ """
+ The browsers which are required to move a TestPlanVersion to RECOMMENDED phase.
+ """
+ recommendedBrowsers: [Browser]!
}
"""
@@ -188,28 +204,6 @@ const graphqlSchema = gql`
releasedAt: Timestamp
}
- # TODO: Determine if needed following 2021 Working Mode changes
- # https://github.com/w3c/aria-at/wiki/Working-Mode
- # """
- # To make sure Test Plans are relevant, they need to be reviewed by community
- # stakeholders such as AT vendors.
- # """
- # enum TestPlanVersionStatus {
- # """
- # Default value meaning the source has been imported and the test plan
- # can be reviewed internally.
- # """
- # DRAFT
- # """
- # Receiving review from stakeholders.
- # """
- # CANDIDATE
- # """
- # Wide review phase complete and ready to record test results.
- # """
- # RECOMMENDED
- # }
-
"""
A suite of tests which keeps its identity as it evolves over time.
"""
@@ -236,25 +230,11 @@ const graphqlSchema = gql`
Gets the most recent version imported from the test plan's directory.
"""
latestTestPlanVersion: TestPlanVersion
- # latestTestPlanVersion(
- # # TODO: Waiting for TestPlanVersionStatus to be implemented
- # status: TestPlanVersionStatus
- #
- # # TODO: determine if we need to filter test plans with no results
- # testPlanReportStatuses: [TestPlanReportStatus]
- # ): TestPlanVersion
"""
Gets all historic versions of the test plan.
"""
testPlanVersions: [TestPlanVersion]!
- # testPlanVersions(
- # # TODO: Waiting for TestPlanVersionStatus to be implemented
- # status: TestPlanVersionStatus
- #
- # # TODO: determine if we need to filter test plans with no results
- # testPlanReportStatuses: [TestPlanReportStatus]
- # ): [TestPlanVersion]!
}
"""
@@ -296,21 +276,16 @@ const graphqlSchema = gql`
The title of the TestPlan at this point in time.
"""
title: String
- # TODO: Waiting for TestPlanVersionStatus to be needed
- # status: TestPlanVersionStatus!
-
- # TODO: decide whether to use this approach, since we think gitShas are
- # a bit intimidating and hard to use.
- # """
- # A version label set in the ARIA-AT repo. The app will only import new
- # versions when that label has changed.
- # """
- # label: String!
"""
- See TestPlanVersionStatus type for more information.
+ See TestPlanVersionPhase type for more information.
"""
phase: TestPlanVersionPhase!
"""
+ Date of when the TestPlanVersion last updated to the 'Draft'
+ phase.
+ """
+ draftPhaseReachedAt: Timestamp
+ """
Date of when the TestPlanVersion was last updated to the 'Candidate'
phase.
"""
@@ -327,6 +302,10 @@ const graphqlSchema = gql`
"""
recommendedPhaseTargetDate: Timestamp
"""
+ The date when the TestPlanVersion was deprecated.
+ """
+ deprecatedAt: Timestamp
+ """
The TestPlan this TestPlanVersion is a snapshot of.
"""
testPlan: TestPlan!
@@ -342,6 +321,7 @@ const graphqlSchema = gql`
gitMessage: String! # TODO: remove if using version labels
"""
The date (originating in Git) corresponding to the Git sha's commit.
+ This can also be considered as the time for when R & D was complete
"""
updatedAt: Timestamp!
# TODO: consider moving to the Scenario type if we support multiple
@@ -362,9 +342,15 @@ const graphqlSchema = gql`
tests: [Test]!
"""
The TestPlanReports attached to the TestPlanVersion. There will always
- be a unique combination of AT + Browser + TestPlanVersion
+ be a unique combination of AT + Browser + TestPlanVersion.
+
+ isFinal is used to check if a TestPlanReport has been "Marked as Final",
+ indicated by TestPlanReport.markedFinalAt existence.
+ None value indicates to return all.
+ True value indicates to return the reports which only have an markedFinalAt date.
+ False value indicates to return the reports which have no markedFinalAt date.
"""
- testPlanReports(isCurrentPhase: Boolean): [TestPlanReport]!
+ testPlanReports(isFinal: Boolean): [TestPlanReport]!
}
"""
@@ -764,27 +750,6 @@ const graphqlSchema = gql`
testResultsLength: Int!
}
- """
- The life-cycle of a TestPlanReport from the point it is created by an admin
- until it is saved an available to the public on the reports page.
- """
- enum TestPlanReportStatus {
- """
- Accepting new TestPlanRuns from testers.
- """
- DRAFT
- """
- Testing is complete and consistent, and ready to be displayed in the
- Candidate Tests and Reports section of the app.
- """
- CANDIDATE
- """
- Testing is complete and consistent, and ready to be displayed in the
- Reports section of the app as being recommended.
- """
- RECOMMENDED
- }
-
"""
Tests, as we envision them, should not leave any room for interpretation. If
a conflict between results is found, the report cannot be published until
@@ -850,10 +815,6 @@ const graphqlSchema = gql`
"""
id: ID!
"""
- See TestPlanReportStatus type for more information.
- """
- status: TestPlanReportStatus!
- """
The snapshot of a TestPlan to use.
"""
testPlanVersion: TestPlanVersion!
@@ -889,7 +850,7 @@ const graphqlSchema = gql`
Scenario if the output or unexpected behaviors do not match, or even at
the level of an Assertion, if the result of an assertion does not match.
- These conflicts must be resolved before the status can change from
+ These conflicts must be resolved before the TestPlanVersion phase can change from
DRAFT to CANDIDATE.
"""
conflicts: [TestPlanReportConflict]!
@@ -928,6 +889,16 @@ const graphqlSchema = gql`
The point at which an admin created the TestPlanReport.
"""
createdAt: Timestamp!
+ """
+ This is marked with the date when an admin has determined that all conflicts on the
+ TestPlanReport have been resolved and indicates that the TestPlanReport is ready
+ to be included when the entire TestPlanVersion is advanced to the "CANDIDATE" phase.
+ """
+ markedFinalAt: Timestamp
+ """
+ Indicated by TestPlanReport.markedFinalAt existence, after a report has been "marked as final".
+ """
+ isFinal: Boolean!
}
"""
@@ -1029,14 +1000,15 @@ const graphqlSchema = gql`
testPlanVersion(id: ID): TestPlanVersion
"""
Load multiple TestPlanReports, with the optional ability to filter by
- status, atId and testPlanVersionId.
+ TestPlanVersionPhase, atId, testPlanVersionId and if the report is marked as final.
See TestPlanReport type for more information.
"""
testPlanReports(
- statuses: [TestPlanReportStatus]
+ testPlanVersionPhases: [TestPlanVersionPhase]
testPlanVersionId: ID
testPlanVersionIds: [ID]
atId: ID
+ isFinal: Boolean
): [TestPlanReport]!
"""
Get a TestPlanReport by ID.
@@ -1122,10 +1094,16 @@ const graphqlSchema = gql`
"""
deleteTestPlanRun(userId: ID!): PopulatedData!
"""
- Update the report status. Remember that all conflicts must be resolved
- when setting the status to CANDIDATE. Only available to admins.
+ Updates the markedFinalAt date. This must be set before a TestPlanReport can
+ be advanced to CANDIDATE. All conflicts must also be resolved.
+ Only available to admins.
+ """
+ markAsFinal: PopulatedData!
+ """
+ Remove the TestPlanReport's markedFinalAt date. This allows the TestPlanReport
+ to be worked on in the Test Queue page again if was previously marked as final.
"""
- updateStatus(status: TestPlanReportStatus!): PopulatedData!
+ unmarkAsFinal: PopulatedData!
"""
Update the report to a specific TestPlanVersion id.
"""
@@ -1136,12 +1114,6 @@ const graphqlSchema = gql`
input: TestPlanReportInput!
): PopulatedData!
"""
- Update the report status for multiple TestPlanReports. Remember that all
- conflicts must be resolved when setting the status to CANDIDATE. Only
- available to admins.
- """
- bulkUpdateStatus(status: TestPlanReportStatus!): [PopulatedData]!
- """
Move the vendor review status from READY to IN PROGRESS
or IN PROGRESS to APPROVED
"""
@@ -1159,12 +1131,13 @@ const graphqlSchema = gql`
type TestPlanVersionOperations {
"""
Update the test plan version phase. Remember that all conflicts must be resolved
- when setting the status to CANDIDATE. Only available to admins.
+ when setting the phase to CANDIDATE. Only available to admins.
"""
updatePhase(
phase: TestPlanVersionPhase!
candidatePhaseReachedAt: Timestamp
recommendedPhaseTargetDate: Timestamp
+ testPlanVersionDataToIncludeId: ID
): PopulatedData!
"""
Update the test plan version recommended phase target date.
@@ -1256,10 +1229,7 @@ const graphqlSchema = gql`
Adds a report with the given TestPlanVersion, AT and Browser, and a
state of "DRAFT", resulting in the report appearing in the Test Queue.
In the case an identical report already exists, it will be returned
- without changes and without affecting existing results. In the case an
- identical report exists but with a status of "CANDIDATE" or "RECOMMENDED",
- it will be given a status of "DRAFT" and will therefore be pulled back
- into the queue with its results unaffected.
+ without changes and without affecting existing results.
"""
findOrCreateTestPlanReport(
"""
diff --git a/server/handlebars/helpers/index.js b/server/handlebars/embed/helpers/index.js
similarity index 100%
rename from server/handlebars/helpers/index.js
rename to server/handlebars/embed/helpers/index.js
diff --git a/server/handlebars/public/script.js b/server/handlebars/embed/public/script.js
similarity index 100%
rename from server/handlebars/public/script.js
rename to server/handlebars/embed/public/script.js
diff --git a/server/handlebars/public/style.css b/server/handlebars/embed/public/style.css
similarity index 97%
rename from server/handlebars/public/style.css
rename to server/handlebars/embed/public/style.css
index 113468f2d..9778cd559 100644
--- a/server/handlebars/public/style.css
+++ b/server/handlebars/embed/public/style.css
@@ -43,10 +43,10 @@ h3#report-title {
text-align: center;
}
-#embed-report-status-container {
+#embed-report-phase-container {
margin-bottom: 1em;
}
-#embed-report-status-container summary:focus-visible {
+#embed-report-phase-container summary:focus-visible {
outline-offset: -2px;
outline: 2px solid #3a86d1;
}
diff --git a/server/handlebars/views/layouts/index.hbs b/server/handlebars/embed/views/layouts/index.hbs
similarity index 100%
rename from server/handlebars/views/layouts/index.hbs
rename to server/handlebars/embed/views/layouts/index.hbs
diff --git a/server/handlebars/views/main.hbs b/server/handlebars/embed/views/main.hbs
similarity index 98%
rename from server/handlebars/views/main.hbs
rename to server/handlebars/embed/views/main.hbs
index 98e127d80..ca8459d4c 100644
--- a/server/handlebars/views/main.hbs
+++ b/server/handlebars/embed/views/main.hbs
@@ -7,8 +7,8 @@
{{/if}}
{{#unless dataEmpty}}
- {{#if (isCandidate status)}}
-
+ {{#if (isCandidate phase)}}
+ Warning! Unapproved Report
The information in this report is generated from candidate tests developed and run by the ARIA-AT Project.
@@ -20,7 +20,7 @@
{{else}}
-
+ Recommended Report
The information in this report is generated from recommended tests.
diff --git a/server/handlebars/test-review/helpers/index.js b/server/handlebars/test-review/helpers/index.js
new file mode 100644
index 000000000..5d5097338
--- /dev/null
+++ b/server/handlebars/test-review/helpers/index.js
@@ -0,0 +1,12 @@
+module.exports = {
+ arrayLength: function (array) {
+ if (!array) return 0;
+ if (!array.length) return 0;
+ return array.length;
+ },
+ formatArrayJoinSeparator: function (array, separator) {
+ if (!array) return '';
+ if (!array.length) return '';
+ return array.join(separator);
+ }
+};
diff --git a/server/handlebars/test-review/public/script.js b/server/handlebars/test-review/public/script.js
new file mode 100644
index 000000000..ef457f607
--- /dev/null
+++ b/server/handlebars/test-review/public/script.js
@@ -0,0 +1,69 @@
+// eslint-disable-next-line no-unused-vars
+class TestWindow {
+ /**
+ * Based on https://github.com/w3c/aria-at/blob/master/tests/resources/aria-at-test-window.mjs
+ * @param {object} options
+ * @param {Window | null} [options.window]
+ * @param {string} options.pageUri
+ * @param {TestWindowHooks} [options.hooks]
+ */
+ constructor({ window = null, pageUri, hooks }) {
+ /** @type {Window | null} */
+ this.window = window;
+
+ /** @type {string} */
+ this.pageUri = pageUri;
+
+ /** @type {TestWindowHooks} */
+ this.hooks = {
+ windowOpened: () => {},
+ windowClosed: () => {},
+ ...hooks
+ };
+ }
+
+ open() {
+ this.window = window.open(
+ this.pageUri,
+ '_blank',
+ 'toolbar=0,location=0,menubar=0,width=800,height=800'
+ );
+
+ this.hooks.windowOpened();
+
+ this.prepare();
+ }
+
+ prepare() {
+ if (!this.window) {
+ return;
+ }
+
+ if (this.window.closed) {
+ this.window = undefined;
+ this.hooks.windowClosed();
+ return;
+ }
+
+ if (
+ this.window.location.origin !== window.location.origin || // make sure the origin is the same, and prevent this from firing on an 'about' page
+ this.window.document.readyState !== 'complete'
+ ) {
+ window.setTimeout(() => {
+ this.prepare();
+ }, 100);
+ return;
+ }
+
+ // If the window is closed, re-enable open popup button
+ this.window.onunload = () => {
+ window.setTimeout(() => this.prepare(), 100);
+ };
+ }
+
+ close() {
+ if (this.window) {
+ this.window.close();
+ }
+ }
+}
diff --git a/server/handlebars/test-review/public/style.css b/server/handlebars/test-review/public/style.css
new file mode 100644
index 000000000..098879de2
--- /dev/null
+++ b/server/handlebars/test-review/public/style.css
@@ -0,0 +1,3 @@
+.commit-message {
+ margin: 0;
+}
diff --git a/server/handlebars/test-review/scripts/at-commands.js b/server/handlebars/test-review/scripts/at-commands.js
new file mode 100644
index 000000000..5b1e62ad3
--- /dev/null
+++ b/server/handlebars/test-review/scripts/at-commands.js
@@ -0,0 +1,144 @@
+/** @deprecated See aria-at-test-io-format.mjs */
+
+const commandsMap = require('../../../resources/commands.json');
+
+/**
+ * Class for getting AT-specific instructions for a test against a design pattern.
+ * @deprecated See aria-at-test-io-format.mjs:CommandsInput
+ */
+module.exports = class CommandsAPI {
+ /**
+ * Creates an API to get AT-specific instructions for a design pattern.
+ * @param {object} commands - A data structure which is a nested object with the following format:
+ * {
+ * task: {
+ * mode: {
+ * at: [
+ * key-command (string corresponding to export in keys.mjs),
+ * optional additional instructions to list after key command (string),
+ * ]
+ * }
+ * }
+ * }
+ */
+ constructor(commands, ats) {
+ if (!commands) {
+ throw new Error(
+ 'You must initialize commandsAPI with a commands data object'
+ );
+ }
+
+ if (!ats) {
+ throw new Error(
+ 'You must initialize commandsAPI with a ats data object'
+ );
+ }
+
+ this.AT_COMMAND_MAP = commands;
+
+ this.MODE_INSTRUCTIONS = {
+ reading: {
+ jaws: `Verify the Virtual Cursor is active by pressing ${this.commandString(
+ 'ALT_DELETE'
+ )}. If it is not, exit Forms Mode to activate the Virtual Cursor by pressing ${this.commandString(
+ 'ESC'
+ )}.`,
+ nvda: `Ensure NVDA is in browse mode by pressing ${this.commandString(
+ 'ESC'
+ )}. Note: This command has no effect if NVDA is already in browse mode.`,
+ voiceover_macos: `Toggle Quick Nav ON by pressing the ${this.commandString(
+ 'LEFT'
+ )} and ${this.commandString('RIGHT')} keys at the same time.`
+ },
+ interaction: {
+ jaws: `Verify the PC Cursor is active by pressing ${this.commandString(
+ 'ALT_DELETE'
+ )}. If it is not, turn off the Virtual Cursor by pressing ${this.commandString(
+ 'INS_Z'
+ )}.`,
+ nvda: `If NVDA did not make the focus mode sound when the test page loaded, press ${this.commandString(
+ 'INS_SPACE'
+ )} to turn focus mode on.`,
+ voiceover_macos: `Toggle Quick Nav OFF by pressing the ${this.commandString(
+ 'LEFT'
+ )} and ${this.commandString('RIGHT')} keys at the same time.`
+ }
+ };
+
+ this.ats = ats;
+ }
+
+ commandString(keyId) {
+ return commandsMap.find(command => command.id === keyId).text;
+ }
+
+ /**
+ * Get AT-specific instruction
+ * @param {string} mode - The mode of the screen reader, "reading" or "interaction"
+ * @param {string} task - The task of the test.
+ * @param {string} assitiveTech - The assistive technology.
+ * @return {Array} - A list of commands (strings)
+ */
+ getATCommands(mode, task, assistiveTech) {
+ if (!this.AT_COMMAND_MAP[task]) {
+ throw new Error(
+ `Task "${task}" does not exist, please add to at-commands or correct your spelling.`
+ );
+ } else if (!this.AT_COMMAND_MAP[task][mode]) {
+ throw new Error(
+ `Mode "${mode}" instructions for task "${task}" does not exist, please add to at-commands or correct your spelling.`
+ );
+ }
+
+ let commandsData =
+ this.AT_COMMAND_MAP[task][mode][assistiveTech.key] || [];
+ let commands = [];
+
+ for (let c of commandsData) {
+ let innerCommands = [];
+ let commandSequence = c[0].split(',');
+ for (let command of commandSequence) {
+ command = this.commandString(command);
+ if (typeof command === 'undefined') {
+ throw new Error(
+ `Key instruction identifier "${c}" for AT "${assistiveTech.name}", mode "${mode}", task "${task}" is not an available identified. Update you commands.json file to the correct identifier or add your identifier to resources/keys.mjs.`
+ );
+ }
+
+ let furtherInstruction = c[1];
+ command = furtherInstruction
+ ? `${command} ${furtherInstruction}`
+ : command;
+ innerCommands.push(command);
+ }
+ commands.push(innerCommands.join(', then '));
+ }
+
+ return commands;
+ }
+
+ /**
+ * Get AT-specific mode switching instructions
+ * @param {string} mode - The mode of the screen reader, "reading" or "interaction"
+ * @param {string} assistiveTech - The assistive technology.
+ * @return {string} - Instructions for switching into the correct mode.
+ */
+ getModeInstructions(mode, assistiveTech) {
+ if (
+ this.MODE_INSTRUCTIONS[mode] &&
+ this.MODE_INSTRUCTIONS[mode][assistiveTech.key]
+ ) {
+ return this.MODE_INSTRUCTIONS[mode][assistiveTech.key];
+ }
+ return '';
+ }
+
+ /**
+ * Get AT-specific instruction
+ * @param {string} at - an assitve technology with any capitalization
+ * @return {string} - if this API knows instructions for `at`, it will return the `at` with proper capitalization
+ */
+ isKnownAT(at) {
+ return this.ats.find(o => o.key === at.toLowerCase());
+ }
+};
diff --git a/server/handlebars/test-review/scripts/command-tuples-at-mode-task-lookup.js b/server/handlebars/test-review/scripts/command-tuples-at-mode-task-lookup.js
new file mode 100644
index 000000000..9b5d42329
--- /dev/null
+++ b/server/handlebars/test-review/scripts/command-tuples-at-mode-task-lookup.js
@@ -0,0 +1,38 @@
+///
+///
+
+'use strict';
+
+/**
+ * Create command lookup object and file.
+ * @param {AriaATValidated.Command[]} commands
+ * @returns {AriaATFile.CommandTuplesATModeTaskLookup}
+ */
+function createCommandTuplesATModeTaskLookup(commands) {
+ const data = commands.reduce((carry, command) => {
+ const commandTask = carry[command.task] || {};
+ const commandTaskMode = commandTask[command.target.mode] || {};
+ const commandTaskModeAT = commandTaskMode[command.target.at.key] || [];
+ const commandTuples = command.commands.map(({ id, extraInstruction }) =>
+ extraInstruction ? [id, extraInstruction] : [id]
+ );
+ return {
+ ...carry,
+ [command.task]: {
+ ...commandTask,
+ [command.target.mode]: {
+ ...commandTaskMode,
+ [command.target.at.key]: [
+ ...commandTaskModeAT,
+ ...commandTuples
+ ]
+ }
+ }
+ };
+ }, {});
+
+ return data;
+}
+
+exports.createCommandTuplesATModeTaskLookup =
+ createCommandTuplesATModeTaskLookup;
diff --git a/server/handlebars/test-review/scripts/parse-command-csv-row.js b/server/handlebars/test-review/scripts/parse-command-csv-row.js
new file mode 100644
index 000000000..5a071af4e
--- /dev/null
+++ b/server/handlebars/test-review/scripts/parse-command-csv-row.js
@@ -0,0 +1,70 @@
+///
+///
+
+'use strict';
+
+/**
+ * @param {AriaATCSV.Command} commandRow
+ * @returns {AriaATParsed.Command}
+ */
+function parseCommandCSVRow(commandRow) {
+ return {
+ testId: Number(commandRow.testId),
+ task: commandRow.task.replace(/[';]/g, '').trim().toLowerCase(),
+ target: {
+ at: {
+ key: commandRow.at.trim().toLowerCase(),
+ raw: commandRow.at
+ },
+ mode: commandRow.mode.trim().toLowerCase()
+ },
+ commands: [
+ commandRow.commandA,
+ commandRow.commandB,
+ commandRow.commandC,
+ commandRow.commandD,
+ commandRow.commandE,
+ commandRow.commandF,
+ commandRow.commandG,
+ commandRow.commandH,
+ commandRow.commandI,
+ commandRow.commandJ,
+ commandRow.commandK,
+ commandRow.commandL,
+ commandRow.commandM,
+ commandRow.commandN,
+ commandRow.commandO,
+ commandRow.commandP,
+ commandRow.commandQ,
+ commandRow.commandR,
+ commandRow.commandS,
+ commandRow.commandT,
+ commandRow.commandU,
+ commandRow.commandV,
+ commandRow.commandW,
+ commandRow.commandX,
+ commandRow.commandY,
+ commandRow.commandZ
+ ]
+ .filter(Boolean)
+ .map(command => {
+ const paranIndex = command.indexOf('(');
+ if (paranIndex >= 0) {
+ return {
+ id: command.substring(0, paranIndex).trim(),
+ extraInstruction: command.substring(paranIndex).trim()
+ };
+ }
+ return {
+ id: command.trim()
+ };
+ })
+ .map(({ id, ...rest }) => ({
+ id,
+ keypresses: id.split(',').map(id => ({ id })),
+ ...rest
+ }))
+ };
+}
+
+exports.parseCommandCSVRow = parseCommandCSVRow;
diff --git a/server/handlebars/test-review/views/layouts/index.hbs b/server/handlebars/test-review/views/layouts/index.hbs
new file mode 100644
index 000000000..fc886f6bf
--- /dev/null
+++ b/server/handlebars/test-review/views/layouts/index.hbs
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Test plan review for pattern: {{pattern}}
+
+
+
+
+{{{body}}}
+
+
+
diff --git a/server/handlebars/test-review/views/main.hbs b/server/handlebars/test-review/views/main.hbs
new file mode 100644
index 000000000..3504edf0f
--- /dev/null
+++ b/server/handlebars/test-review/views/main.hbs
@@ -0,0 +1,192 @@
+
+
+
+
+
+ Test plan review for pattern: {{pattern}}
+
+
+
+
+
+
Test plan review for pattern: {{pattern}} ({{arrayLength tests}} tests)
+
Version created on: {{commitDate}}
+
Summary of commit:
+{{{commitMessage}}}
+
+
+
+{{#each tests}}
+
Test {{testNumber}}: {{{name}}}
+
+
Mode: {{mode}}
+
Applies to: {{formatArrayJoinSeparator allRelevantAts ", "}}