+ This page provides a view of the latest test plan version
+ information, and where they currently are in the{' '}
+
+ ARIA-AT Community Group’s review process
+
+ .
+ Use this page to manage Test Plans in the Test Queue and their
+ phases.
+
+
+
-
- Invalid Request | ARIA-AT
-
-
-
-
Whoops! Unable to complete request
-
-
-
- );
+ return (
+
+
+ Invalid Request | ARIA-AT
+
+
+
+
Whoops! Unable to complete request
+
+
+
+ );
};
export default InvalidRequest;
diff --git a/client/components/LoadingSpinner/LoadingSpinner.css b/client/components/LoadingSpinner/LoadingSpinner.css
index 054a18b6d..ffddd3d81 100644
--- a/client/components/LoadingSpinner/LoadingSpinner.css
+++ b/client/components/LoadingSpinner/LoadingSpinner.css
@@ -1,51 +1,51 @@
.spinner-container {
- --side-length: 80px;
- --color: #007bff;
+ --side-length: 80px;
+ --color: #007bff;
}
.spinner {
- animation: rotate 2s linear infinite;
- z-index: 2;
- width: var(--side-length);
- height: var(--side-length);
+ animation: rotate 2s linear infinite;
+ z-index: 2;
+ width: var(--side-length);
+ height: var(--side-length);
}
.spinner .path {
- stroke: var(--color);
- stroke-linecap: round;
- animation: dash 1.5s ease-in-out infinite;
+ stroke: var(--color);
+ stroke-linecap: round;
+ animation: dash 1.5s ease-in-out infinite;
}
.percentage {
- display: flex;
- width: var(--side-length);
- height: var(--side-length);
- position: absolute;
- justify-content: center;
- align-items: center;
- line-height: 1;
- color: var(--color);
- font-family: system-ui;
- font-weight: bold;
+ display: flex;
+ width: var(--side-length);
+ height: var(--side-length);
+ position: absolute;
+ justify-content: center;
+ align-items: center;
+ line-height: 1;
+ color: var(--color);
+ font-family: system-ui;
+ font-weight: bold;
}
@keyframes rotate {
- 100% {
- transform: rotate(360deg);
- }
+ 100% {
+ transform: rotate(360deg);
+ }
}
@keyframes dash {
- 0% {
- stroke-dasharray: 1, 150;
- stroke-dashoffset: 0;
- }
- 50% {
- stroke-dasharray: 90, 150;
- stroke-dashoffset: -35;
- }
- 100% {
- stroke-dasharray: 90, 150;
- stroke-dashoffset: -124;
- }
+ 0% {
+ stroke-dasharray: 1, 150;
+ stroke-dashoffset: 0;
+ }
+ 50% {
+ stroke-dasharray: 90, 150;
+ stroke-dashoffset: -35;
+ }
+ 100% {
+ stroke-dasharray: 90, 150;
+ stroke-dashoffset: -124;
+ }
}
diff --git a/client/components/LoadingSpinner/LoadingSpinner.jsx b/client/components/LoadingSpinner/LoadingSpinner.jsx
index e7a646b1c..a586d90c8 100644
--- a/client/components/LoadingSpinner/LoadingSpinner.jsx
+++ b/client/components/LoadingSpinner/LoadingSpinner.jsx
@@ -3,38 +3,38 @@ import './LoadingSpinner.css';
import PropTypes from 'prop-types';
const LoadingSpinner = ({ percentage = null, className = '' }) => {
- return (
-
- {percentage === null ? null : (
-
- {percentage}%
-
- )}
-
-
- Spinner
-
-
+ return (
+
+ {percentage === null ? null : (
+
+ {percentage}%
- );
+ )}
+
+
+ Spinner
+
+
+
+ );
};
LoadingSpinner.propTypes = {
- className: PropTypes.string,
- percentage: PropTypes.number
+ className: PropTypes.string,
+ percentage: PropTypes.number
};
export default LoadingSpinner;
diff --git a/client/components/ManageBotRunDialog/ManageBotRunDialog.css b/client/components/ManageBotRunDialog/ManageBotRunDialog.css
index 88bccce50..b909a2d05 100644
--- a/client/components/ManageBotRunDialog/ManageBotRunDialog.css
+++ b/client/components/ManageBotRunDialog/ManageBotRunDialog.css
@@ -1,5 +1,5 @@
.manage-bot-run-dialog {
- width: 60vw;
- max-width: 900px;
- min-width: 600px;
+ width: 60vw;
+ max-width: 900px;
+ min-width: 600px;
}
diff --git a/client/components/ManageBotRunDialog/MarkBotRunFinishedButton/index.jsx b/client/components/ManageBotRunDialog/MarkBotRunFinishedButton/index.jsx
index 78d032249..bcec9dd67 100644
--- a/client/components/ManageBotRunDialog/MarkBotRunFinishedButton/index.jsx
+++ b/client/components/ManageBotRunDialog/MarkBotRunFinishedButton/index.jsx
@@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
import { Button } from 'react-bootstrap';
import { useMutation, useQuery } from '@apollo/client';
import {
- SUBMIT_TEST_RESULT_MUTATION,
- TEST_PLAN_RUN_RESULT_IDS
+ SUBMIT_TEST_RESULT_MUTATION,
+ TEST_PLAN_RUN_RESULT_IDS
} from './queries';
import { LoadingStatus, useTriggerLoad } from '../../common/LoadingStatus';
import { useTestPlanRunValidatedAssertionCounts } from '../../../hooks/useTestPlanRunValidatedAssertionCounts';
@@ -12,82 +12,82 @@ import BasicModal from '../../common/BasicModal';
import { useTestPlanRunIsFinished } from '../../../hooks/useTestPlanRunIsFinished';
const MarkBotRunFinishedButton = ({ testPlanRun, onClick = () => {} }) => {
- const {
- data: testPlanRunCompletionQuery,
- loading,
- refetch
- } = useQuery(TEST_PLAN_RUN_RESULT_IDS, {
- variables: {
- testPlanRunId: testPlanRun.id
- }
- });
+ const {
+ data: testPlanRunCompletionQuery,
+ loading,
+ refetch
+ } = useQuery(TEST_PLAN_RUN_RESULT_IDS, {
+ variables: {
+ testPlanRunId: testPlanRun.id
+ }
+ });
- const { triggerLoad, loadingMessage } = useTriggerLoad();
+ const { triggerLoad, loadingMessage } = useTriggerLoad();
- const { totalValidatedAssertions, totalPossibleAssertions } =
- useTestPlanRunValidatedAssertionCounts(testPlanRun);
+ const { totalValidatedAssertions, totalPossibleAssertions } =
+ useTestPlanRunValidatedAssertionCounts(testPlanRun);
- const [submitTestResult] = useMutation(SUBMIT_TEST_RESULT_MUTATION);
+ const [submitTestResult] = useMutation(SUBMIT_TEST_RESULT_MUTATION);
- const [showConfirmation, setShowConfirmation] = useState(false);
+ const [showConfirmation, setShowConfirmation] = useState(false);
- const { runIsFinished } = useTestPlanRunIsFinished(testPlanRun.id);
+ const { runIsFinished } = useTestPlanRunIsFinished(testPlanRun.id);
- if (loading || !testPlanRunCompletionQuery) {
- return null;
- }
+ if (loading || !testPlanRunCompletionQuery) {
+ return null;
+ }
- const handleClick = async () => {
- await triggerLoad(async () => {
- for (const testResult of testPlanRunCompletionQuery.testPlanRun
- .testResults) {
- await submitTestResult({
- variables: {
- id: testResult.id
- }
- });
- }
- }, 'Marking test plan run as finished');
- await refetch();
- await onClick();
- };
+ const handleClick = async () => {
+ await triggerLoad(async () => {
+ for (const testResult of testPlanRunCompletionQuery.testPlanRun
+ .testResults) {
+ await submitTestResult({
+ variables: {
+ id: testResult.id
+ }
+ });
+ }
+ }, 'Marking test plan run as finished');
+ await refetch();
+ await onClick();
+ };
- const buttonDisabled =
- totalPossibleAssertions === 0 ||
- totalValidatedAssertions < totalPossibleAssertions ||
- runIsFinished;
+ const buttonDisabled =
+ totalPossibleAssertions === 0 ||
+ totalValidatedAssertions < totalPossibleAssertions ||
+ runIsFinished;
- return (
-
- setShowConfirmation(false)}
- handleHide={() => setShowConfirmation(false)}
- useOnHide={true}
- closeLabel="No"
- show={showConfirmation}
- actions={[
- {
- label: 'Yes',
- onClick: handleClick
- }
- ]}
- />
- setShowConfirmation(true)}
- disabled={buttonDisabled}
- >
- Mark as finished
-
-
- );
+ return (
+
+ setShowConfirmation(false)}
+ handleHide={() => setShowConfirmation(false)}
+ useOnHide={true}
+ closeLabel="No"
+ show={showConfirmation}
+ actions={[
+ {
+ label: 'Yes',
+ onClick: handleClick
+ }
+ ]}
+ />
+ setShowConfirmation(true)}
+ disabled={buttonDisabled}
+ >
+ Mark as finished
+
+
+ );
};
MarkBotRunFinishedButton.propTypes = {
- testPlanRun: PropTypes.object.isRequired,
- onClick: PropTypes.func
+ testPlanRun: PropTypes.object.isRequired,
+ onClick: PropTypes.func
};
export default MarkBotRunFinishedButton;
diff --git a/client/components/ManageBotRunDialog/MarkBotRunFinishedButton/queries.js b/client/components/ManageBotRunDialog/MarkBotRunFinishedButton/queries.js
index b15c975a2..5d5311bf0 100644
--- a/client/components/ManageBotRunDialog/MarkBotRunFinishedButton/queries.js
+++ b/client/components/ManageBotRunDialog/MarkBotRunFinishedButton/queries.js
@@ -1,28 +1,28 @@
const { gql } = require('@apollo/client');
export const TEST_PLAN_RUN_RESULT_IDS = gql`
- query TestPlanRunIds($testPlanRunId: ID!) {
- testPlanRun(id: $testPlanRunId) {
- id
- testResults {
- id
- }
- }
+ query TestPlanRunIds($testPlanRunId: ID!) {
+ testPlanRun(id: $testPlanRunId) {
+ id
+ testResults {
+ id
+ }
}
+ }
`;
export const SUBMIT_TEST_RESULT_MUTATION = gql`
- mutation SubmitTestResult($id: ID!) {
- testResult(id: $id) {
- submitTestResult(input: { id: $id }) {
- testPlanRun {
- id
- testResults {
- id
- completedAt
- }
- }
- }
+ mutation SubmitTestResult($id: ID!) {
+ testResult(id: $id) {
+ submitTestResult(input: { id: $id }) {
+ testPlanRun {
+ id
+ testResults {
+ id
+ completedAt
+ }
}
+ }
}
+ }
`;
diff --git a/client/components/ManageBotRunDialog/RetryCanceledCollectionsButton/index.jsx b/client/components/ManageBotRunDialog/RetryCanceledCollectionsButton/index.jsx
index 36815c558..a842b8142 100644
--- a/client/components/ManageBotRunDialog/RetryCanceledCollectionsButton/index.jsx
+++ b/client/components/ManageBotRunDialog/RetryCanceledCollectionsButton/index.jsx
@@ -5,45 +5,45 @@ import { useMutation } from '@apollo/client';
import { RETRY_CANCELED_COLLECTIONS } from '../queries';
const RetryCanceledCollectionsButton = ({
- collectionJob,
- onClick = () => {}
+ collectionJob,
+ onClick = () => {}
}) => {
- if (!collectionJob) {
- return null;
- }
+ if (!collectionJob) {
+ return null;
+ }
- const [retryCanceledCollections] = useMutation(RETRY_CANCELED_COLLECTIONS, {
- variables: { collectionJobId: collectionJob.id }
- });
+ const [retryCanceledCollections] = useMutation(RETRY_CANCELED_COLLECTIONS, {
+ variables: { collectionJobId: collectionJob.id }
+ });
- const handleClick = async () => {
- await retryCanceledCollections();
- await onClick();
- };
+ const handleClick = async () => {
+ await retryCanceledCollections();
+ await onClick();
+ };
- return (
-
- Retry Cancelled Collections
-
- );
+ return (
+
+ Retry Cancelled Collections
+
+ );
};
RetryCanceledCollectionsButton.propTypes = {
- collectionJob: PropTypes.shape({
- id: PropTypes.string,
- status: PropTypes.oneOf([
- 'QUEUED',
- 'RUNNING',
- 'CANCELLED',
- 'COMPLETED',
- 'ERROR'
- ])
- }),
- onClick: PropTypes.func
+ collectionJob: PropTypes.shape({
+ id: PropTypes.string,
+ status: PropTypes.oneOf([
+ 'QUEUED',
+ 'RUNNING',
+ 'CANCELLED',
+ 'COMPLETED',
+ 'ERROR'
+ ])
+ }),
+ onClick: PropTypes.func
};
export default RetryCanceledCollectionsButton;
diff --git a/client/components/ManageBotRunDialog/StopRunningCollectionButton/index.jsx b/client/components/ManageBotRunDialog/StopRunningCollectionButton/index.jsx
index 586c7270d..b985996fd 100644
--- a/client/components/ManageBotRunDialog/StopRunningCollectionButton/index.jsx
+++ b/client/components/ManageBotRunDialog/StopRunningCollectionButton/index.jsx
@@ -6,55 +6,55 @@ import { CANCEL_COLLECTION_JOB } from '../queries';
import { LoadingStatus, useTriggerLoad } from '../../common/LoadingStatus';
const StopRunningCollectionButton = ({ collectionJob, onClick = () => {} }) => {
- if (!collectionJob) {
- return null;
- }
+ if (!collectionJob) {
+ return null;
+ }
- const { triggerLoad, loadingMessage } = useTriggerLoad();
+ const { triggerLoad, loadingMessage } = useTriggerLoad();
- const [cancelCollectionJob] = useMutation(CANCEL_COLLECTION_JOB, {
- variables: {
- collectionJobId: collectionJob.id
- }
- });
+ const [cancelCollectionJob] = useMutation(CANCEL_COLLECTION_JOB, {
+ variables: {
+ collectionJobId: collectionJob.id
+ }
+ });
- const handleClick = async () => {
- await triggerLoad(async () => {
- await cancelCollectionJob();
- }, 'Cancelling Collection Job');
- await onClick();
- };
+ const handleClick = async () => {
+ await triggerLoad(async () => {
+ await cancelCollectionJob();
+ }, 'Cancelling Collection Job');
+ await onClick();
+ };
- return (
-
-
- Stop Running
-
-
- );
+ return (
+
+
+ Stop Running
+
+
+ );
};
StopRunningCollectionButton.propTypes = {
- collectionJob: PropTypes.shape({
- id: PropTypes.string,
- status: PropTypes.oneOf([
- 'QUEUED',
- 'RUNNING',
- 'CANCELLED',
- 'COMPLETED',
- 'ERROR'
- ]).isRequired
- }),
- onClick: PropTypes.func
+ collectionJob: PropTypes.shape({
+ id: PropTypes.string,
+ status: PropTypes.oneOf([
+ 'QUEUED',
+ 'RUNNING',
+ 'CANCELLED',
+ 'COMPLETED',
+ 'ERROR'
+ ]).isRequired
+ }),
+ onClick: PropTypes.func
};
export default StopRunningCollectionButton;
diff --git a/client/components/ManageBotRunDialog/ViewLogsButton/index.jsx b/client/components/ManageBotRunDialog/ViewLogsButton/index.jsx
new file mode 100644
index 000000000..8b82273f5
--- /dev/null
+++ b/client/components/ManageBotRunDialog/ViewLogsButton/index.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button } from 'react-bootstrap';
+
+const ViewLogsButton = ({ externalLogsUrl }) => {
+ const onClick = () => {
+ window.open(externalLogsUrl, '_blank');
+ };
+
+ return (
+
+ View Log
+
+ );
+};
+
+ViewLogsButton.propTypes = {
+ externalLogsUrl: PropTypes.string
+};
+
+export default ViewLogsButton;
diff --git a/client/components/ManageBotRunDialog/WithButton.jsx b/client/components/ManageBotRunDialog/WithButton.jsx
index 920cd9498..ea6f9018a 100644
--- a/client/components/ManageBotRunDialog/WithButton.jsx
+++ b/client/components/ManageBotRunDialog/WithButton.jsx
@@ -7,56 +7,53 @@ import ManageBotRunDialog from '.';
import { useTestPlanRunIsFinished } from '../../hooks/useTestPlanRunIsFinished';
const ManageBotRunDialogWithButton = ({
- testPlanRun,
- testPlanReportId,
- runnableTestsLength,
- testers,
- includeIcon = false,
- onChange
+ testPlanRun,
+ testPlanReportId,
+ runnableTestsLength,
+ testers,
+ onChange
}) => {
- const { runIsFinished } = useTestPlanRunIsFinished(testPlanRun.id);
- const [showDialog, setShowDialog] = useState(false);
- if (runIsFinished) {
- return null;
- }
+ const { runIsFinished } = useTestPlanRunIsFinished(testPlanRun.id);
+ const [showDialog, setShowDialog] = useState(false);
+ if (runIsFinished) {
+ return null;
+ }
- return (
- <>
-
{
- setShowDialog(true);
- }}
- >
- {/* TODO: Include by default after removing Test Queue v1 content */}
- {includeIcon ? : null}
- Manage {testPlanRun?.tester?.username} Run
-
- {showDialog ? (
-
{
- await onChange();
- setShowDialog(false);
- }}
- />
- ) : null}
- >
- );
+ return (
+ <>
+ {
+ setShowDialog(true);
+ }}
+ >
+
+ Manage {testPlanRun?.tester?.username} Run
+
+ {showDialog ? (
+ {
+ await onChange();
+ setShowDialog(false);
+ }}
+ />
+ ) : null}
+ >
+ );
};
ManageBotRunDialogWithButton.propTypes = {
- testPlanRun: PropTypes.object.isRequired,
- testPlanReportId: PropTypes.string.isRequired,
- runnableTestsLength: PropTypes.number.isRequired,
- testers: PropTypes.array.isRequired,
- includeIcon: PropTypes.bool,
- onChange: PropTypes.func.isRequired
+ testPlanRun: PropTypes.object.isRequired,
+ testPlanReportId: PropTypes.string.isRequired,
+ runnableTestsLength: PropTypes.number.isRequired,
+ testers: PropTypes.array.isRequired,
+ onChange: PropTypes.func.isRequired
};
export default ManageBotRunDialogWithButton;
diff --git a/client/components/ManageBotRunDialog/index.jsx b/client/components/ManageBotRunDialog/index.jsx
index b2cd54584..154a8db6b 100644
--- a/client/components/ManageBotRunDialog/index.jsx
+++ b/client/components/ManageBotRunDialog/index.jsx
@@ -1,12 +1,12 @@
import React, { useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import BasicModal from '../common/BasicModal';
-import AssignTesterDropdown from '../TestQueue/AssignTesterDropdown';
+import AssignTesterDropdown from '../common/AssignTesterDropdown';
import { useMutation, useQuery } from '@apollo/client';
import {
- COLLECTION_JOB_ID_BY_TEST_PLAN_RUN_ID_QUERY,
- DELETE_TEST_PLAN_RUN,
- TEST_PLAN_REPORT_ASSIGNED_TESTERS_QUERY
+ COLLECTION_JOB_ID_BY_TEST_PLAN_RUN_ID_QUERY,
+ DELETE_TEST_PLAN_RUN,
+ TEST_PLAN_REPORT_ASSIGNED_TESTERS_QUERY
} from './queries';
import DeleteButton from '../common/DeleteButton';
import BotRunTestStatusList from '../BotRunTestStatusList';
@@ -15,162 +15,175 @@ import './ManageBotRunDialog.css';
import MarkBotRunFinishedButton from './MarkBotRunFinishedButton';
import RetryCanceledCollectionsButton from './RetryCanceledCollectionsButton';
import StopRunningCollectionButton from './StopRunningCollectionButton';
+import ViewLogsButton from './ViewLogsButton';
const ManageBotRunDialog = ({
- testPlanReportId,
- runnableTestsLength,
- testPlanRun,
- testers,
- show,
- setShow,
- onChange
+ testPlanReportId,
+ runnableTestsLength,
+ testPlanRun,
+ testers,
+ show,
+ setShow,
+ onChange
}) => {
- const [showDeleteDialog, setShowDeleteDialog] = useState(false);
+ const [showDeleteDialog, setShowDeleteDialog] = useState(false);
- const { data: collectionJobQuery } = useQuery(
- COLLECTION_JOB_ID_BY_TEST_PLAN_RUN_ID_QUERY,
- {
- variables: {
- testPlanRunId: testPlanRun.id
- },
- fetchPolicy: 'cache-and-network'
- }
- );
+ const { data: collectionJobQuery } = useQuery(
+ COLLECTION_JOB_ID_BY_TEST_PLAN_RUN_ID_QUERY,
+ {
+ variables: {
+ testPlanRunId: testPlanRun.id
+ },
+ fetchPolicy: 'cache-and-network'
+ }
+ );
- const [deleteTestPlanRun] = useMutation(DELETE_TEST_PLAN_RUN, {
- variables: {
- testPlanReportId: testPlanReportId,
- userId: testPlanRun.tester.id
- }
- });
+ const [deleteTestPlanRun] = useMutation(DELETE_TEST_PLAN_RUN, {
+ variables: {
+ testPlanReportId: testPlanReportId,
+ userId: testPlanRun.tester.id
+ }
+ });
+
+ const { data: testPlanReportAssignedTestersQuery } = useQuery(
+ TEST_PLAN_REPORT_ASSIGNED_TESTERS_QUERY,
+ {
+ variables: {
+ testPlanReportId
+ },
+ fetchPolicy: 'cache-and-network'
+ }
+ );
+
+ const possibleReassignees = useMemo(
+ () =>
+ testers.filter(
+ t =>
+ !t.isBot &&
+ !testPlanReportAssignedTestersQuery?.testPlanReport.draftTestPlanRuns.some(
+ d => d.tester.id === t.id
+ )
+ ),
+ [testers, testPlanReportAssignedTestersQuery]
+ );
- const { data: testPlanReportAssignedTestersQuery } = useQuery(
- TEST_PLAN_REPORT_ASSIGNED_TESTERS_QUERY,
- {
- variables: {
- testPlanReportId
- },
- fetchPolicy: 'cache-and-network'
+ const actions = useMemo(() => {
+ return [
+ {
+ component: AssignTesterDropdown,
+ props: {
+ testPlanReportId: testPlanReportId,
+ testPlanRun: testPlanRun,
+ possibleTesters: possibleReassignees,
+ label: 'Assign To ...',
+ onChange
}
- );
+ },
+ {
+ component: ViewLogsButton,
+ props: {
+ externalLogsUrl:
+ collectionJobQuery?.collectionJobByTestPlanRunId?.externalLogsUrl
+ }
+ },
+ {
+ component: StopRunningCollectionButton,
+ props: {
+ collectionJob: collectionJobQuery?.collectionJobByTestPlanRunId,
+ onClick: onChange
+ }
+ },
+ {
+ component: RetryCanceledCollectionsButton,
+ props: {
+ collectionJob: collectionJobQuery?.collectionJobByTestPlanRunId,
+ onClick: onChange
+ }
+ },
+ {
+ component: MarkBotRunFinishedButton,
+ props: {
+ testPlanRun: testPlanRun,
+ onClick: onChange
+ }
+ },
+ {
+ component: DeleteButton,
+ props: {
+ ariaLabel: 'Delete bot run',
+ onClick: () => setShowDeleteDialog(true)
+ }
+ }
+ ];
+ }, [
+ testPlanReportId,
+ testPlanRun,
+ possibleReassignees,
+ onChange,
+ collectionJobQuery
+ ]);
- const possibleReassignees = useMemo(
- () =>
- testers.filter(
- t =>
- !t.isBot &&
- !testPlanReportAssignedTestersQuery?.testPlanReport.draftTestPlanRuns.some(
- d => d.tester.id === t.id
- )
- ),
- [testers, testPlanReportAssignedTestersQuery]
- );
+ const deleteConfirmationContent = (
+ <>
+
+ The test plan run for {testPlanRun.tester.username} will be
+ deleted.
+
+
+ Please press Delete to confirm this action.
+
+ >
+ );
+ const content = (
+
+ );
- const actions = useMemo(() => {
- return [
- {
- component: AssignTesterDropdown,
- props: {
- testPlanReportId: testPlanReportId,
- testPlanRun: testPlanRun,
- possibleTesters: possibleReassignees,
- label: 'Assign To ...',
- onChange
- }
- },
- {
- component: StopRunningCollectionButton,
- props: {
- collectionJob:
- collectionJobQuery?.collectionJobByTestPlanRunId,
- onClick: onChange
- }
- },
- {
- component: RetryCanceledCollectionsButton,
- props: {
- collectionJob:
- collectionJobQuery?.collectionJobByTestPlanRunId,
- onClick: onChange
- }
- },
- {
- component: MarkBotRunFinishedButton,
- props: {
- testPlanRun: testPlanRun,
- onClick: onChange
- }
- },
- {
- component: DeleteButton,
- props: {
- ariaLabel: 'Delete bot run',
- onClick: () => setShowDeleteDialog(true)
- }
+ return (
+ <>
+ setShowDeleteDialog(false)}
+ useOnHide={true}
+ cancelButton={true}
+ title={`You are about to delete the run for ${testPlanRun.tester?.username}`}
+ content={deleteConfirmationContent}
+ actions={[
+ {
+ label: 'Delete',
+ variant: 'danger',
+ onClick: async () => {
+ await deleteTestPlanRun();
+ await onChange();
+ setShowDeleteDialog(false);
}
- ];
- }, [testPlanReportId, testPlanRun, possibleReassignees, onChange]);
- const deleteConfirmationContent = (
- <>
-
- The test plan run for {testPlanRun.tester.username} will
- be deleted.
-
-
- Please press Delete to confirm this action.
-
- >
- );
- const content = (
-
- );
-
- return (
- <>
- setShowDeleteDialog(false)}
- useOnHide={true}
- cancelButton={true}
- title={`You are about to delete the run for ${testPlanRun.tester?.username}`}
- content={deleteConfirmationContent}
- actions={[
- {
- label: 'Delete',
- variant: 'danger',
- onClick: async () => {
- await deleteTestPlanRun();
- await onChange();
- setShowDeleteDialog(false);
- }
- }
- ]}
- />
- setShow(false)}
- useOnHide={true}
- title={`Manage ${testPlanRun.tester?.username} Run`}
- cancelButton={false}
- content={content}
- dialogClassName="manage-bot-run-dialog"
- actions={actions}
- />
- >
- );
+ }
+ ]}
+ />
+ setShow(false)}
+ useOnHide={true}
+ title={`Manage ${testPlanRun.tester?.username} Run`}
+ cancelButton={false}
+ content={content}
+ dialogClassName="manage-bot-run-dialog"
+ actions={actions}
+ />
+ >
+ );
};
ManageBotRunDialog.propTypes = {
- testPlanRun: PropTypes.object.isRequired,
- show: PropTypes.bool.isRequired,
- setShow: PropTypes.func.isRequired,
- testers: PropTypes.array.isRequired,
- testPlanReportId: PropTypes.string.isRequired,
- runnableTestsLength: PropTypes.number.isRequired,
- onChange: PropTypes.func.isRequired
+ testPlanRun: PropTypes.object.isRequired,
+ show: PropTypes.bool.isRequired,
+ setShow: PropTypes.func.isRequired,
+ testers: PropTypes.array.isRequired,
+ testPlanReportId: PropTypes.string.isRequired,
+ runnableTestsLength: PropTypes.number.isRequired,
+ onChange: PropTypes.func.isRequired
};
export default ManageBotRunDialog;
diff --git a/client/components/ManageBotRunDialog/queries.js b/client/components/ManageBotRunDialog/queries.js
index 734a63db1..dba46068e 100644
--- a/client/components/ManageBotRunDialog/queries.js
+++ b/client/components/ManageBotRunDialog/queries.js
@@ -1,57 +1,58 @@
import { gql } from '@apollo/client';
export const COLLECTION_JOB_ID_BY_TEST_PLAN_RUN_ID_QUERY = gql`
- query CollectionJobIdByTestPlanRunId($testPlanRunId: ID!) {
- collectionJobByTestPlanRunId(testPlanRunId: $testPlanRunId) {
- id
- status
- }
+ query CollectionJobIdByTestPlanRunId($testPlanRunId: ID!) {
+ collectionJobByTestPlanRunId(testPlanRunId: $testPlanRunId) {
+ id
+ status
+ externalLogsUrl
}
+ }
`;
export const TEST_PLAN_REPORT_ASSIGNED_TESTERS_QUERY = gql`
- query TestPlanReportAssignedTesters($testPlanReportId: ID!) {
- testPlanReport(id: $testPlanReportId) {
- id
- draftTestPlanRuns {
- tester {
- id
- }
- }
+ query TestPlanReportAssignedTesters($testPlanReportId: ID!) {
+ testPlanReport(id: $testPlanReportId) {
+ id
+ draftTestPlanRuns {
+ tester {
+ id
}
+ }
}
+ }
`;
export const CANCEL_COLLECTION_JOB = gql`
- mutation CancelCollectionJob($collectionJobId: ID!) {
- collectionJob(id: $collectionJobId) {
- cancelCollectionJob {
- id
- status
- }
- }
+ mutation CancelCollectionJob($collectionJobId: ID!) {
+ collectionJob(id: $collectionJobId) {
+ cancelCollectionJob {
+ id
+ status
+ }
}
+ }
`;
export const DELETE_TEST_PLAN_RUN = gql`
- mutation DeleteTestPlanRun($testPlanReportId: ID!, $userId: ID!) {
- testPlanReport(id: $testPlanReportId) {
- deleteTestPlanRun(userId: $userId) {
- testPlanRun {
- id
- }
- }
+ mutation DeleteTestPlanRun($testPlanReportId: ID!, $userId: ID!) {
+ testPlanReport(id: $testPlanReportId) {
+ deleteTestPlanRun(userId: $userId) {
+ testPlanRun {
+ id
}
+ }
}
+ }
`;
export const RETRY_CANCELED_COLLECTIONS = gql`
- mutation RetryCanceledCollections($collectionJobId: ID!) {
- collectionJob(id: $collectionJobId) {
- retryCanceledCollections {
- id
- status
- }
- }
+ mutation RetryCanceledCollections($collectionJobId: ID!) {
+ collectionJob(id: $collectionJobId) {
+ retryCanceledCollections {
+ id
+ status
+ }
}
+ }
`;
diff --git a/client/components/ManageTestQueue/AddTestPlans.jsx b/client/components/ManageTestQueue/AddTestPlans.jsx
index d0ee8458b..cfd929294 100644
--- a/client/components/ManageTestQueue/AddTestPlans.jsx
+++ b/client/components/ManageTestQueue/AddTestPlans.jsx
@@ -7,337 +7,299 @@ import { gitUpdatedDateToString } from '@client/utils/gitUtils';
import PropTypes from 'prop-types';
const AddTestPlans = ({
- ats = [],
- testPlanVersions = [],
- triggerUpdate = () => {}
+ ats = [],
+ testPlanVersions = [],
+ triggerUpdate = () => {}
}) => {
- const [allTestPlans, setAllTestPlans] = useState([]);
- const [allTestPlanVersions, setAllTestPlanVersions] = useState([]);
+ const [allTestPlans, setAllTestPlans] = useState([]);
+ const [allTestPlanVersions, setAllTestPlanVersions] = useState([]);
- const [selectedTestPlanVersionId, setSelectedTestPlanVersionId] =
- useState('');
- const [matchingTestPlanVersions, setMatchingTestPlanVersions] = useState(
- []
- );
-
- const [selectedAtId, setSelectedAtId] = useState('');
- const [selectedBrowserId, setSelectedBrowserId] = useState('');
- const [
- selectedAtVersionExactOrMinimum,
- setSelectedAtVersionExactOrMinimum
- ] = useState('Exact Version');
- const [selectedReportAtVersionId, setSelectedReportAtVersionId] =
- useState(null);
- const [
- showMinimumAtVersionErrorMessage,
- setShowMinimumAtVersionErrorMessage
- ] = useState(false);
+ const [selectedTestPlanVersionId, setSelectedTestPlanVersionId] =
+ useState('');
+ const [matchingTestPlanVersions, setMatchingTestPlanVersions] = useState([]);
- useEffect(() => {
- // Prevent allTestPlanVersions and filteredTestPlanVersions from being unnecessarily overwritten
- if (allTestPlanVersions.length) return;
+ const [selectedAtId, setSelectedAtId] = useState('');
+ const [selectedBrowserId, setSelectedBrowserId] = useState('');
+ const [selectedAtVersionExactOrMinimum, setSelectedAtVersionExactOrMinimum] =
+ useState('Exact Version');
+ const [selectedReportAtVersionId, setSelectedReportAtVersionId] =
+ useState(null);
+ const [
+ showMinimumAtVersionErrorMessage,
+ setShowMinimumAtVersionErrorMessage
+ ] = useState(false);
- const _allTestPlanVersions = testPlanVersions
- .map(version => ({ ...version }))
- .flat();
+ useEffect(() => {
+ // Prevent allTestPlanVersions and filteredTestPlanVersions from being unnecessarily overwritten
+ if (allTestPlanVersions.length) return;
- // Get valid test plans by removing duplicate entries from different
- // test plan versions of the same test plan being imported multiple times
- const _allTestPlans = _allTestPlanVersions
- .filter(
- (v, i, a) =>
- a.findIndex(
- t =>
- t.title === v.title &&
- t.testPlan.directory === v.testPlan.directory
- ) === i
- )
- .map(({ id, title, testPlan }) => ({
- id,
- title,
- directory: testPlan.directory
- }))
- // sort by the testPlanVersion titles
- .sort((a, b) => (a.title < b.title ? -1 : 1));
+ const _allTestPlanVersions = testPlanVersions
+ .map(version => ({ ...version }))
+ .flat();
- // mark the first testPlanVersion as selected
- if (_allTestPlans.length) {
- const plan = _allTestPlans[0];
- updateMatchingTestPlanVersions(plan.id, _allTestPlanVersions);
- }
+ // Get valid test plans by removing duplicate entries from different
+ // test plan versions of the same test plan being imported multiple times
+ const _allTestPlans = _allTestPlanVersions
+ .filter(
+ (v, i, a) =>
+ a.findIndex(
+ t =>
+ t.title === v.title &&
+ t.testPlan.directory === v.testPlan.directory
+ ) === i
+ )
+ .map(({ id, title, testPlan }) => ({
+ id,
+ title,
+ directory: testPlan.directory
+ }))
+ // sort by the testPlanVersion titles
+ .sort((a, b) => (a.title < b.title ? -1 : 1));
- setAllTestPlans(_allTestPlans);
- setAllTestPlanVersions(_allTestPlanVersions);
- }, [testPlanVersions]);
+ // mark the first testPlanVersion as selected
+ if (_allTestPlans.length) {
+ const plan = _allTestPlans[0];
+ updateMatchingTestPlanVersions(plan.id, _allTestPlanVersions);
+ }
- const updateMatchingTestPlanVersions = (value, allTestPlanVersions) => {
- // update test plan versions based on selected test plan
- const retrievedTestPlanVersion = allTestPlanVersions.find(
- item => item.id === value
- );
+ setAllTestPlans(_allTestPlans);
+ setAllTestPlanVersions(_allTestPlanVersions);
+ }, [testPlanVersions]);
- // find the versions that apply and pre-set these
- const matchingTestPlanVersions = allTestPlanVersions
- .filter(
- item =>
- item.title === retrievedTestPlanVersion.title &&
- item.testPlan.directory ===
- retrievedTestPlanVersion.testPlan.directory &&
- item.phase !== 'DEPRECATED' &&
- item.phase !== 'RD'
- )
- .sort((a, b) =>
- new Date(a.updatedAt) > new Date(b.updatedAt) ? -1 : 1
- );
- setMatchingTestPlanVersions(matchingTestPlanVersions);
+ const updateMatchingTestPlanVersions = (value, allTestPlanVersions) => {
+ // update test plan versions based on selected test plan
+ const retrievedTestPlanVersion = allTestPlanVersions.find(
+ item => item.id === value
+ );
- if (matchingTestPlanVersions.length)
- setSelectedTestPlanVersionId(matchingTestPlanVersions[0].id);
- else setSelectedTestPlanVersionId(null);
- };
+ // find the versions that apply and pre-set these
+ const matchingTestPlanVersions = allTestPlanVersions
+ .filter(
+ item =>
+ item.title === retrievedTestPlanVersion.title &&
+ item.testPlan.directory ===
+ retrievedTestPlanVersion.testPlan.directory &&
+ item.phase !== 'DEPRECATED' &&
+ item.phase !== 'RD'
+ )
+ .sort((a, b) => (new Date(a.updatedAt) > new Date(b.updatedAt) ? -1 : 1));
+ setMatchingTestPlanVersions(matchingTestPlanVersions);
- const onTestPlanVersionChange = e => {
- const { value } = e.target;
- setShowMinimumAtVersionErrorMessage(false);
- setSelectedAtVersionExactOrMinimum('Exact Version');
- setSelectedTestPlanVersionId(value);
- };
+ if (matchingTestPlanVersions.length)
+ setSelectedTestPlanVersionId(matchingTestPlanVersions[0].id);
+ else setSelectedTestPlanVersionId(null);
+ };
- const onAtChange = e => {
- const { value } = e.target;
- setShowMinimumAtVersionErrorMessage(false);
- setSelectedAtId(value);
- setSelectedReportAtVersionId(null);
- };
+ const onTestPlanVersionChange = e => {
+ const { value } = e.target;
+ setShowMinimumAtVersionErrorMessage(false);
+ setSelectedAtVersionExactOrMinimum('Exact Version');
+ setSelectedTestPlanVersionId(value);
+ };
- const onReportAtVersionIdChange = e => {
- const { value } = e.target;
- setSelectedReportAtVersionId(value);
- };
+ const onAtChange = e => {
+ const { value } = e.target;
+ setShowMinimumAtVersionErrorMessage(false);
+ setSelectedAtId(value);
+ setSelectedReportAtVersionId(null);
+ };
- const onBrowserChange = e => {
- const { value } = e.target;
- setSelectedBrowserId(value);
- };
+ const onReportAtVersionIdChange = e => {
+ const { value } = e.target;
+ setSelectedReportAtVersionId(value);
+ };
- const selectedTestPlanVersion = allTestPlanVersions.find(
- ({ id }) => id === selectedTestPlanVersionId
- );
+ const onBrowserChange = e => {
+ const { value } = e.target;
+ setSelectedBrowserId(value);
+ };
- const exactOrMinimumAtVersion = ats
- .find(item => item.id === selectedAtId)
- ?.atVersions.find(item => item.id === selectedReportAtVersionId);
+ const selectedTestPlanVersion = allTestPlanVersions.find(
+ ({ id }) => id === selectedTestPlanVersionId
+ );
- return (
-
-
- Select a test plan, assistive technology and browser to add a
- new test plan report to the test queue.
-
-
-
-
- Test Plan
-
- {
- const { value } = e.target;
- setShowMinimumAtVersionErrorMessage(false);
- setSelectedAtVersionExactOrMinimum('Exact Version');
- updateMatchingTestPlanVersions(
- value,
- allTestPlanVersions
- );
- }}
- >
- {allTestPlans.map(item => (
-
- {item.title || `"${item.directory}"`}
-
- ))}
-
-
-
-
- Test Plan Version
-
-
- {matchingTestPlanVersions.length ? (
- matchingTestPlanVersions.map(item => (
-
- {gitUpdatedDateToString(item.updatedAt)}{' '}
- {item.gitMessage} (
- {item.gitSha.substring(0, 7)})
-
- ))
- ) : (
- Versions in R&D or Deprecated
- )}
-
-
-
{/* blank grid cell */}
-
-
- Assistive Technology
-
-
-
- Select an Assistive Technology
-
- {ats.map(item => (
-
- {item.name}
-
- ))}
-
-
-
-
- Assistive Technology Version
-
-
-
{
- if (
- selectedTestPlanVersion?.phase ===
- 'RECOMMENDED' &&
- exactOrMinimum === 'Minimum Version'
- ) {
- setShowMinimumAtVersionErrorMessage(true);
- return;
- }
+ const exactOrMinimumAtVersion = ats
+ .find(item => item.id === selectedAtId)
+ ?.atVersions.find(item => item.id === selectedReportAtVersionId);
- setSelectedAtVersionExactOrMinimum(
- exactOrMinimum
- );
- }}
- />
-
-
- Select AT Version
-
- {ats
- .find(at => at.id === selectedAtId)
- ?.atVersions.map(item => (
-
- {item.name}
-
- ))}
-
- {showMinimumAtVersionErrorMessage &&
- selectedTestPlanVersion?.phase === 'RECOMMENDED' ? (
-
- The selected test plan version is in the
- recommended phase and only exact versions can be
- chosen.
-
- ) : null}
-
-
-
-
- Browser
-
-
-
- Select a Browser
-
- {ats
- .find(at => at.id === selectedAtId)
- ?.browsers.map(item => (
-
- {item.name}
-
- ))}
-
-
-
- item.id === selectedTestPlanVersionId
- )}
- at={ats.find(item => item.id === selectedAtId)}
- exactAtVersion={
- selectedAtVersionExactOrMinimum === 'Exact Version'
- ? exactOrMinimumAtVersion
- : null
- }
- minimumAtVersion={
- selectedAtVersionExactOrMinimum === 'Minimum Version'
- ? exactOrMinimumAtVersion
- : null
- }
- browser={ats
- .find(at => at.id === selectedAtId)
- ?.browsers.find(
- browser => browser.id === selectedBrowserId
- )}
- triggerUpdate={triggerUpdate}
- disabled={
- !selectedTestPlanVersionId ||
- !selectedAtId ||
- !selectedReportAtVersionId ||
- !selectedBrowserId
+ return (
+
+
+ Select a test plan, assistive technology and browser to add a new test
+ plan report to the test queue.
+
+
+
+ Test Plan
+ {
+ const { value } = e.target;
+ setShowMinimumAtVersionErrorMessage(false);
+ setSelectedAtVersionExactOrMinimum('Exact Version');
+ updateMatchingTestPlanVersions(value, allTestPlanVersions);
+ }}
+ >
+ {allTestPlans.map(item => (
+
+ {item.title || `"${item.directory}"`}
+
+ ))}
+
+
+
+
+ Test Plan Version
+
+
+ {matchingTestPlanVersions.length ? (
+ matchingTestPlanVersions.map(item => (
+
+ {gitUpdatedDateToString(item.updatedAt)} {item.gitMessage} (
+ {item.gitSha.substring(0, 7)})
+
+ ))
+ ) : (
+ Versions in R&D or Deprecated
+ )}
+
+
+
{/* blank grid cell */}
+
+
+ Assistive Technology
+
+
+
+ Select an Assistive Technology
+
+ {ats.map(item => (
+
+ {item.name}
+
+ ))}
+
+
+
+
+ Assistive Technology Version
+
+
+
{
+ if (
+ selectedTestPlanVersion?.phase === 'RECOMMENDED' &&
+ exactOrMinimum === 'Minimum Version'
+ ) {
+ setShowMinimumAtVersionErrorMessage(true);
+ return;
}
+
+ setSelectedAtVersionExactOrMinimum(exactOrMinimum);
+ }}
/>
-
- );
+
+
+ Select AT Version
+
+ {ats
+ .find(at => at.id === selectedAtId)
+ ?.atVersions.map(item => (
+
+ {item.name}
+
+ ))}
+
+ {showMinimumAtVersionErrorMessage &&
+ selectedTestPlanVersion?.phase === 'RECOMMENDED' ? (
+
+ The selected test plan version is in the recommended phase and
+ only exact versions can be chosen.
+
+ ) : null}
+
+
+
+ Browser
+
+
+ Select a Browser
+
+ {ats
+ .find(at => at.id === selectedAtId)
+ ?.browsers.map(item => (
+
+ {item.name}
+
+ ))}
+
+
+
+ item.id === selectedTestPlanVersionId
+ )}
+ at={ats.find(item => item.id === selectedAtId)}
+ exactAtVersion={
+ selectedAtVersionExactOrMinimum === 'Exact Version'
+ ? exactOrMinimumAtVersion
+ : null
+ }
+ minimumAtVersion={
+ selectedAtVersionExactOrMinimum === 'Minimum Version'
+ ? exactOrMinimumAtVersion
+ : null
+ }
+ browser={ats
+ .find(at => at.id === selectedAtId)
+ ?.browsers.find(browser => browser.id === selectedBrowserId)}
+ triggerUpdate={triggerUpdate}
+ disabled={
+ !selectedTestPlanVersionId ||
+ !selectedAtId ||
+ !selectedReportAtVersionId ||
+ !selectedBrowserId
+ }
+ />
+
+ );
};
AddTestPlans.propTypes = {
- ats: PropTypes.arrayOf(
+ ats: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ browsers: PropTypes.arrayOf(
PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- browsers: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- })
- ).isRequired
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
})
- ).isRequired,
- testPlanVersions: PropTypes.array,
- triggerUpdate: PropTypes.func
+ ).isRequired
+ })
+ ).isRequired,
+ testPlanVersions: PropTypes.array,
+ triggerUpdate: PropTypes.func
};
export default AddTestPlans;
diff --git a/client/components/ManageTestQueue/ManageAtVersions.jsx b/client/components/ManageTestQueue/ManageAtVersions.jsx
index c97211ae5..289293780 100644
--- a/client/components/ManageTestQueue/ManageAtVersions.jsx
+++ b/client/components/ManageTestQueue/ManageAtVersions.jsx
@@ -8,444 +8,423 @@ import UpdateVersionModal from '@components/common/UpdateVersionModal';
import { convertStringToDate } from '@client/utils/formatter';
import { useMutation } from '@apollo/client';
import {
- ADD_AT_VERSION_MUTATION,
- DELETE_AT_VERSION_MUTATION,
- EDIT_AT_VERSION_MUTATION
-} from '@components/TestQueue/queries';
+ ADD_AT_VERSION_MUTATION,
+ DELETE_AT_VERSION_MUTATION,
+ EDIT_AT_VERSION_MUTATION
+} from './queries';
import { useTriggerLoad } from '@components/common/LoadingStatus';
import { THEMES, useThemedModal } from '@client/hooks/useThemedModal';
import PropTypes from 'prop-types';
const ManageAtVersions = ({ ats = [], triggerUpdate = () => {} }) => {
- const { triggerLoad } = useTriggerLoad();
- const {
- themedModal,
- showThemedModal,
- setShowThemedModal,
- setThemedModalTitle,
- setThemedModalContent,
- setThemedModalType,
- setThemedModalActions,
- setThemedModalShowCloseAction,
- focus,
- setFocusRef,
- hideThemedModal
- } = useThemedModal({
- type: THEMES.WARNING,
- title: 'Error Updating Assistive Technology Version'
- });
-
- const loadedAts = useRef(false);
-
- const [selectedAtId, setSelectedAtId] = useState('1');
- const [selectedAtVersions, setSelectedAtVersions] = useState([]);
- const [selectedAtVersionId, setSelectedAtVersionId] = useState('');
-
- const [addAtVersion] = useMutation(ADD_AT_VERSION_MUTATION);
- const [editAtVersion] = useMutation(EDIT_AT_VERSION_MUTATION);
- const [deleteAtVersion] = useMutation(DELETE_AT_VERSION_MUTATION);
-
- // Update modal state values
- const [showUpdateVersionModal, setShowUpdateVersionModal] = useState(false);
- const [updateVersionModalTitle, setUpdateVersionModalTitle] = useState('');
- const [updateVersionModalType, setUpdateVersionModalType] = useState('add');
- const [updateVersionModalVersionText, setUpdateVersionModalVersionText] =
- useState('');
- const [
- updateVersionModalModalDateText,
- setUpdateVersionModalModalDateText
- ] = useState('');
-
- // Feedback modal state values
- const [showFeedbackModal, setShowFeedbackModal] = useState(false);
- const [feedbackModalTitle, setFeedbackModalTitle] = useState('');
- const [feedbackModalContent, setFeedbackModalContent] = useState(<>>);
-
- useEffect(() => {
- if (ats.length) {
- if (!loadedAts.current) setSelectedAtId(ats[0].id);
-
- // Required during refetch logic around managing AT Versions
- if (!loadedAts.current) setSelectedAtVersions(ats[0].atVersions);
- else {
- setSelectedAtVersions(
- ats.find(item => item.id === selectedAtId).atVersions
- );
- }
-
- if (!loadedAts.current)
- setSelectedAtVersionId(ats[0]?.atVersions[0]?.id);
- loadedAts.current = true;
- }
- }, [ats]);
-
- const getAtVersionFromId = id =>
- selectedAtVersions.find(item => id === item.id);
-
- const showThemedMessage = ({
- title,
- content,
- theme,
- actions = null,
- showCloseAction = false
- }) => {
- setThemedModalTitle(title);
- setThemedModalContent(content);
- setThemedModalType(theme);
- setThemedModalActions(actions);
- setThemedModalShowCloseAction(showCloseAction);
- setShowThemedModal(true);
- };
-
- const showFeedbackMessage = (title, content) => {
- setFeedbackModalTitle(title);
- setFeedbackModalContent(content);
- setShowFeedbackModal(true);
- };
-
- const onAtChange = e => {
- const { value } = e.target;
- if (selectedAtId !== value) {
- setSelectedAtId(value);
- const at = ats.find(item => item.id === value);
- setSelectedAtVersions(at.atVersions);
- setSelectedAtVersionId(at.atVersions[0].id);
- }
- };
-
- const onAtVersionChange = e => {
- const { value } = e.target;
- setSelectedAtVersionId(value);
- };
-
- const onOpenAtVersionModalClick = type => {
- if (type === 'add') {
- const selectedAt = ats.find(item => item.id === selectedAtId);
- setUpdateVersionModalTitle(
- `Add a New Version for ${selectedAt.name}`
- );
- setUpdateVersionModalType('add');
- setUpdateVersionModalVersionText('');
- setUpdateVersionModalModalDateText('');
- setShowUpdateVersionModal(true);
- }
-
- if (type === 'edit') {
- const selectedAt = ats.find(item => item.id === selectedAtId);
- setUpdateVersionModalTitle(
- `Edit ${selectedAt.name} Version ${
- getAtVersionFromId(selectedAtVersionId)?.name
- }`
- );
- setUpdateVersionModalType('edit');
- setUpdateVersionModalVersionText(
- getAtVersionFromId(selectedAtVersionId)?.name
- );
- setUpdateVersionModalModalDateText(
- getAtVersionFromId(selectedAtVersionId)?.releasedAt
- );
- setShowUpdateVersionModal(true);
- }
-
- if (type === 'delete') {
- const theme = 'danger';
-
- const selectedAt = ats.find(item => item.id === selectedAtId);
- showThemedMessage({
- title: `Remove ${selectedAt.name} Version ${
- getAtVersionFromId(selectedAtVersionId)?.name
- }`,
- content: (
- <>
- You are about to remove{' '}
-
- {selectedAt.name} Version{' '}
- {getAtVersionFromId(selectedAtVersionId)?.name}
- {' '}
- from the ARIA-AT App.
- >
- ),
- actions: [
- {
- text: 'Remove',
- action: () => onUpdateAtVersionAction('delete', {})
- }
- ],
- showCloseAction: true,
- theme
- });
- }
- };
-
- const onUpdateAtVersionAction = async (
- actionType,
- { updatedVersionText, updatedDateAvailabilityText }
- ) => {
- const selectedAt = ats.find(item => item.id === selectedAtId);
-
- if (actionType === 'add') {
- const existingAtVersion = selectedAtVersions.find(
- item => item.name.trim() === updatedVersionText.trim()
- );
- if (existingAtVersion) {
- setSelectedAtVersionId(existingAtVersion.id);
-
- onUpdateModalClose();
- showFeedbackMessage(
- 'Existing Assistive Technology Version',
- <>
-
- {selectedAt.name} {updatedVersionText}
- {' '}
- already exists in the system. Please try adding a
- different one.
- >
- );
- return;
- }
-
- onUpdateModalClose();
- await triggerLoad(async () => {
- const addAtVersionData = await addAtVersion({
- variables: {
- atId: selectedAtId,
- name: updatedVersionText,
- releasedAt: convertStringToDate(
- updatedDateAvailabilityText
- )
- }
- });
- setSelectedAtVersionId(
- addAtVersionData.data?.at?.findOrCreateAtVersion?.id
- );
- await triggerUpdate();
- }, 'Adding Assistive Technology Version');
-
- showFeedbackMessage(
- 'Successfully Added Assistive Technology Version',
- <>
- Successfully added{' '}
-
- {selectedAt.name} {updatedVersionText}
-
- .
- >
- );
- }
-
- if (actionType === 'edit') {
- onUpdateModalClose();
- await triggerLoad(async () => {
- await editAtVersion({
- variables: {
- atVersionId: selectedAtVersionId,
- name: updatedVersionText,
- releasedAt: convertStringToDate(
- updatedDateAvailabilityText
- )
- }
- });
- await triggerUpdate();
- }, 'Updating Assistive Technology Version');
-
- showFeedbackMessage(
- 'Successfully Updated Assistive Technology Version',
- <>
- Successfully updated{' '}
-
- {selectedAt.name} {updatedVersionText}
-
- .
- >
- );
- }
-
- if (actionType === 'delete') {
- const deleteAtVersionData = await deleteAtVersion({
- variables: {
- atVersionId: selectedAtVersionId
- }
- });
- if (
- !deleteAtVersionData.data?.atVersion?.deleteAtVersion?.isDeleted
- ) {
- const patternName =
- deleteAtVersionData.data?.atVersion?.deleteAtVersion
- ?.failedDueToTestResults[0]?.testPlanVersion?.title;
- const theme = 'warning';
-
- // Removing an AT Version already in use
- showThemedMessage({
- title: 'Assistive Technology Version already being used',
- content: (
- <>
-
- {selectedAt.name} Version{' '}
- {getAtVersionFromId(selectedAtVersionId)?.name}
- {' '}
- can't be removed because it is already being
- used to test the {patternName} Test Plan.
- >
- ),
- theme
- });
- } else {
- onThemedModalClose();
-
- await triggerLoad(async () => {
- await triggerUpdate();
- }, 'Removing Assistive Technology Version');
-
- // Show confirmation that AT has been deleted
- showFeedbackMessage(
- 'Successfully Removed Assistive Technology Version',
- <>
- Successfully removed version for{' '}
- {selectedAt.name} .
- >
- );
-
- // Reset atVersion to valid existing item
- setSelectedAtVersionId(selectedAt.atVersions[0]?.id);
- }
- }
- };
-
- const onUpdateModalClose = () => {
- setUpdateVersionModalVersionText('');
- setUpdateVersionModalModalDateText('');
- setShowUpdateVersionModal(false);
- focus();
- };
-
- const onThemedModalClose = () => {
- hideThemedModal();
- focus();
- };
-
- return (
+ const { triggerLoad } = useTriggerLoad();
+ const {
+ themedModal,
+ showThemedModal,
+ setShowThemedModal,
+ setThemedModalTitle,
+ setThemedModalContent,
+ setThemedModalType,
+ setThemedModalActions,
+ setThemedModalShowCloseAction,
+ focus,
+ setFocusRef,
+ hideThemedModal
+ } = useThemedModal({
+ type: THEMES.WARNING,
+ title: 'Error Updating Assistive Technology Version'
+ });
+
+ const loadedAts = useRef(false);
+
+ const [selectedAtId, setSelectedAtId] = useState('1');
+ const [selectedAtVersions, setSelectedAtVersions] = useState([]);
+ const [selectedAtVersionId, setSelectedAtVersionId] = useState('');
+
+ const [addAtVersion] = useMutation(ADD_AT_VERSION_MUTATION);
+ const [editAtVersion] = useMutation(EDIT_AT_VERSION_MUTATION);
+ const [deleteAtVersion] = useMutation(DELETE_AT_VERSION_MUTATION);
+
+ // Update modal state values
+ const [showUpdateVersionModal, setShowUpdateVersionModal] = useState(false);
+ const [updateVersionModalTitle, setUpdateVersionModalTitle] = useState('');
+ const [updateVersionModalType, setUpdateVersionModalType] = useState('add');
+ const [updateVersionModalVersionText, setUpdateVersionModalVersionText] =
+ useState('');
+ const [updateVersionModalModalDateText, setUpdateVersionModalModalDateText] =
+ useState('');
+
+ // Feedback modal state values
+ const [showFeedbackModal, setShowFeedbackModal] = useState(false);
+ const [feedbackModalTitle, setFeedbackModalTitle] = useState('');
+ const [feedbackModalContent, setFeedbackModalContent] = useState(<>>);
+
+ useEffect(() => {
+ if (ats.length) {
+ if (!loadedAts.current) setSelectedAtId(ats[0].id);
+
+ // Required during refetch logic around managing AT Versions
+ if (!loadedAts.current) setSelectedAtVersions(ats[0].atVersions);
+ else {
+ setSelectedAtVersions(
+ ats.find(item => item.id === selectedAtId).atVersions
+ );
+ }
+
+ if (!loadedAts.current) setSelectedAtVersionId(ats[0]?.atVersions[0]?.id);
+ loadedAts.current = true;
+ }
+ }, [ats]);
+
+ const getAtVersionFromId = id =>
+ selectedAtVersions.find(item => id === item.id);
+
+ const showThemedMessage = ({
+ title,
+ content,
+ theme,
+ actions = null,
+ showCloseAction = false
+ }) => {
+ setThemedModalTitle(title);
+ setThemedModalContent(content);
+ setThemedModalType(theme);
+ setThemedModalActions(actions);
+ setThemedModalShowCloseAction(showCloseAction);
+ setShowThemedModal(true);
+ };
+
+ const showFeedbackMessage = (title, content) => {
+ setFeedbackModalTitle(title);
+ setFeedbackModalContent(content);
+ setShowFeedbackModal(true);
+ };
+
+ const onAtChange = e => {
+ const { value } = e.target;
+ if (selectedAtId !== value) {
+ setSelectedAtId(value);
+ const at = ats.find(item => item.id === value);
+ setSelectedAtVersions(at.atVersions);
+ setSelectedAtVersionId(at.atVersions[0].id);
+ }
+ };
+
+ const onAtVersionChange = e => {
+ const { value } = e.target;
+ setSelectedAtVersionId(value);
+ };
+
+ const onOpenAtVersionModalClick = type => {
+ if (type === 'add') {
+ const selectedAt = ats.find(item => item.id === selectedAtId);
+ setUpdateVersionModalTitle(`Add a New Version for ${selectedAt.name}`);
+ setUpdateVersionModalType('add');
+ setUpdateVersionModalVersionText('');
+ setUpdateVersionModalModalDateText('');
+ setShowUpdateVersionModal(true);
+ }
+
+ if (type === 'edit') {
+ const selectedAt = ats.find(item => item.id === selectedAtId);
+ setUpdateVersionModalTitle(
+ `Edit ${selectedAt.name} Version ${
+ getAtVersionFromId(selectedAtVersionId)?.name
+ }`
+ );
+ setUpdateVersionModalType('edit');
+ setUpdateVersionModalVersionText(
+ getAtVersionFromId(selectedAtVersionId)?.name
+ );
+ setUpdateVersionModalModalDateText(
+ getAtVersionFromId(selectedAtVersionId)?.releasedAt
+ );
+ setShowUpdateVersionModal(true);
+ }
+
+ if (type === 'delete') {
+ const theme = 'danger';
+
+ const selectedAt = ats.find(item => item.id === selectedAtId);
+ showThemedMessage({
+ title: `Remove ${selectedAt.name} Version ${
+ getAtVersionFromId(selectedAtVersionId)?.name
+ }`,
+ content: (
+ <>
+ You are about to remove{' '}
+
+ {selectedAt.name} Version{' '}
+ {getAtVersionFromId(selectedAtVersionId)?.name}
+ {' '}
+ from the ARIA-AT App.
+ >
+ ),
+ actions: [
+ {
+ text: 'Remove',
+ action: () => onUpdateAtVersionAction('delete', {})
+ }
+ ],
+ showCloseAction: true,
+ theme
+ });
+ }
+ };
+
+ const onUpdateAtVersionAction = async (
+ actionType,
+ { updatedVersionText, updatedDateAvailabilityText }
+ ) => {
+ const selectedAt = ats.find(item => item.id === selectedAtId);
+
+ if (actionType === 'add') {
+ const existingAtVersion = selectedAtVersions.find(
+ item => item.name.trim() === updatedVersionText.trim()
+ );
+ if (existingAtVersion) {
+ setSelectedAtVersionId(existingAtVersion.id);
+
+ onUpdateModalClose();
+ showFeedbackMessage(
+ 'Existing Assistive Technology Version',
+ <>
+
+ {selectedAt.name} {updatedVersionText}
+ {' '}
+ already exists in the system. Please try adding a different one.
+ >
+ );
+ return;
+ }
+
+ onUpdateModalClose();
+ await triggerLoad(async () => {
+ const addAtVersionData = await addAtVersion({
+ variables: {
+ atId: selectedAtId,
+ name: updatedVersionText,
+ releasedAt: convertStringToDate(updatedDateAvailabilityText)
+ }
+ });
+ setSelectedAtVersionId(
+ addAtVersionData.data?.at?.findOrCreateAtVersion?.id
+ );
+ await triggerUpdate();
+ }, 'Adding Assistive Technology Version');
+
+ showFeedbackMessage(
+ 'Successfully Added Assistive Technology Version',
<>
-
-
- Select an assistive technology and manage its versions in
- the ARIA-AT App
-
-
-
-
- Assistive Technology
-
-
- {ats
- .slice()
- .sort((a, b) => a.name.localeCompare(b.name))
- .map(item => (
-
- {item.name}
-
- ))}
-
-
-
-
-
- Available Versions
-
-
- {selectedAtVersions.map(item => (
-
- {item.name}
-
- ))}
-
-
-
- setFocusRef(ref)}
- onClick={() => onOpenAtVersionModalClick('add')}
- >
- Add a New Version
-
- setFocusRef(ref)}
- onClick={() =>
- onOpenAtVersionModalClick('edit')
- }
- >
-
- Edit
-
- setFocusRef(ref)}
- // onClick={onRemoveClick}
- onClick={() =>
- onOpenAtVersionModalClick('delete')
- }
- >
-
- Remove
-
-
-
-
-
-
- {showThemedModal && themedModal}
- {showUpdateVersionModal && (
-
- )}
- {showFeedbackModal && (
- {
- setShowFeedbackModal(false);
- }}
- />
- )}
+ Successfully added{' '}
+
+ {selectedAt.name} {updatedVersionText}
+
+ .
>
- );
+ );
+ }
+
+ if (actionType === 'edit') {
+ onUpdateModalClose();
+ await triggerLoad(async () => {
+ await editAtVersion({
+ variables: {
+ atVersionId: selectedAtVersionId,
+ name: updatedVersionText,
+ releasedAt: convertStringToDate(updatedDateAvailabilityText)
+ }
+ });
+ await triggerUpdate();
+ }, 'Updating Assistive Technology Version');
+
+ showFeedbackMessage(
+ 'Successfully Updated Assistive Technology Version',
+ <>
+ Successfully updated{' '}
+
+ {selectedAt.name} {updatedVersionText}
+
+ .
+ >
+ );
+ }
+
+ if (actionType === 'delete') {
+ const deleteAtVersionData = await deleteAtVersion({
+ variables: {
+ atVersionId: selectedAtVersionId
+ }
+ });
+ if (!deleteAtVersionData.data?.atVersion?.deleteAtVersion?.isDeleted) {
+ const patternName =
+ deleteAtVersionData.data?.atVersion?.deleteAtVersion
+ ?.failedDueToTestResults[0]?.testPlanVersion?.title;
+ const theme = 'warning';
+
+ // Removing an AT Version already in use
+ showThemedMessage({
+ title: 'Assistive Technology Version already being used',
+ content: (
+ <>
+
+ {selectedAt.name} Version{' '}
+ {getAtVersionFromId(selectedAtVersionId)?.name}
+ {' '}
+ can't be removed because it is already being used to test the{' '}
+ {patternName} Test Plan.
+ >
+ ),
+ theme
+ });
+ } else {
+ onThemedModalClose();
+
+ await triggerLoad(async () => {
+ await triggerUpdate();
+ }, 'Removing Assistive Technology Version');
+
+ // Show confirmation that AT has been deleted
+ showFeedbackMessage(
+ 'Successfully Removed Assistive Technology Version',
+ <>
+ Successfully removed version for {selectedAt.name} .
+ >
+ );
+
+ // Reset atVersion to valid existing item
+ setSelectedAtVersionId(selectedAt.atVersions[0]?.id);
+ }
+ }
+ };
+
+ const onUpdateModalClose = () => {
+ setUpdateVersionModalVersionText('');
+ setUpdateVersionModalModalDateText('');
+ setShowUpdateVersionModal(false);
+ focus();
+ };
+
+ const onThemedModalClose = () => {
+ hideThemedModal();
+ focus();
+ };
+
+ return (
+ <>
+
+
+ Select an assistive technology and manage its versions in the ARIA-AT
+ App
+
+
+
+
+ Assistive Technology
+
+
+ {ats
+ .slice()
+ .sort((a, b) => a.name.localeCompare(b.name))
+ .map(item => (
+
+ {item.name}
+
+ ))}
+
+
+
+
+
+ Available Versions
+
+
+ {selectedAtVersions.map(item => (
+
+ {item.name}
+
+ ))}
+
+
+
+ setFocusRef(ref)}
+ onClick={() => onOpenAtVersionModalClick('add')}
+ >
+ Add a New Version
+
+ setFocusRef(ref)}
+ onClick={() => onOpenAtVersionModalClick('edit')}
+ >
+
+ Edit
+
+ setFocusRef(ref)}
+ onClick={() => onOpenAtVersionModalClick('delete')}
+ >
+
+ Remove
+
+
+
+
+
+
+ {showThemedModal && themedModal}
+ {showUpdateVersionModal && (
+
+ )}
+ {showFeedbackModal && (
+ {
+ setShowFeedbackModal(false);
+ }}
+ />
+ )}
+ >
+ );
};
ManageAtVersions.propTypes = {
- ats: PropTypes.arrayOf(
+ ats: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ browsers: PropTypes.arrayOf(
PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- browsers: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- })
- ).isRequired
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
})
- ).isRequired,
- triggerUpdate: PropTypes.func
+ ).isRequired
+ })
+ ).isRequired,
+ triggerUpdate: PropTypes.func
};
export default ManageAtVersions;
diff --git a/client/components/ManageTestQueue/index.jsx b/client/components/ManageTestQueue/index.jsx
index 58b85c5ab..d5aa795af 100644
--- a/client/components/ManageTestQueue/index.jsx
+++ b/client/components/ManageTestQueue/index.jsx
@@ -7,163 +7,163 @@ import ManageAtVersions from '@components/ManageTestQueue/ManageAtVersions';
import AddTestPlans from '@components/ManageTestQueue/AddTestPlans';
export const DisclosureContainer = styled.div`
- // Following directives are related to the ManageTestQueue component
- > span {
- display: block;
- margin-bottom: 1rem;
+ // Following directives are related to the ManageTestQueue component
+ > span {
+ display: block;
+ margin-bottom: 1rem;
+ }
+
+ // Add Test Plan to Test Queue button
+ > button {
+ padding: 0.5rem 1rem;
+ margin-top: 1rem;
+ }
+
+ .disclosure-row-manage-ats {
+ display: grid;
+ grid-auto-flow: column;
+ grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
+ grid-gap: 1rem;
+
+ .ats-container {
+ grid-column: 1 / span 2;
}
- // Add Test Plan to Test Queue button
- > button {
- padding: 0.5rem 1rem;
- margin-top: 1rem;
+ .at-versions-container {
+ display: flex;
+ flex-direction: column;
+ grid-column: 3 / span 3;
}
- .disclosure-row-manage-ats {
- display: grid;
- grid-auto-flow: column;
- grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
- grid-gap: 1rem;
+ .disclosure-buttons-row {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
- .ats-container {
- grid-column: 1 / span 2;
- }
+ > button {
+ margin: 0;
+ padding: 0;
+ color: #275caa;
+ border: none;
+ background-color: transparent;
- .at-versions-container {
- display: flex;
- flex-direction: column;
- grid-column: 3 / span 3;
+ &:nth-of-type(2) {
+ margin-left: auto;
}
- .disclosure-buttons-row {
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
-
- > button {
- margin: 0;
- padding: 0;
- color: #275caa;
- border: none;
- background-color: transparent;
-
- &:nth-of-type(2) {
- margin-left: auto;
- }
-
- // remove button
- &:nth-of-type(3) {
- margin-left: 1rem;
- color: #ce1b4c;
- }
- }
+ // remove button
+ &:nth-of-type(3) {
+ margin-left: 1rem;
+ color: #ce1b4c;
}
+ }
}
+ }
- .disclosure-row-test-plans {
- display: grid;
- row-gap: 0.5rem;
- grid-template-columns: 2fr 2fr 1fr;
- column-gap: 2rem;
+ .disclosure-row-test-plans {
+ display: grid;
+ row-gap: 0.5rem;
+ grid-template-columns: 2fr 2fr 1fr;
+ column-gap: 2rem;
- & > :nth-of-type(3) {
- display: block;
- }
- & > :nth-of-type(5) {
- grid-column: span 2;
- }
+ & > :nth-of-type(3) {
+ display: block;
+ }
+ & > :nth-of-type(5) {
+ grid-column: span 2;
+ }
- @media (max-width: 768px) {
- grid-template-columns: 1fr;
+ @media (max-width: 768px) {
+ grid-template-columns: 1fr;
- & > :nth-of-type(3) {
- display: none;
- }
- & > :nth-of-type(5) {
- grid-column: initial;
- }
- }
+ & > :nth-of-type(3) {
+ display: none;
+ }
+ & > :nth-of-type(5) {
+ grid-column: initial;
+ }
}
-
- .form-group-at-version {
- display: flex;
- flex-wrap: wrap;
- column-gap: 1rem;
- row-gap: 0.75rem;
-
- select {
- width: inherit;
- @media (max-width: 767px) {
- flex-grow: 1;
- }
- }
+ }
+
+ .form-group-at-version {
+ display: flex;
+ flex-wrap: wrap;
+ column-gap: 1rem;
+ row-gap: 0.75rem;
+
+ select {
+ width: inherit;
+ @media (max-width: 767px) {
+ flex-grow: 1;
+ }
}
+ }
- .disclosure-form-label {
- font-weight: bold;
- font-size: 1rem;
- }
+ .disclosure-form-label {
+ font-weight: bold;
+ font-size: 1rem;
+ }
`;
const ManageTestQueue = ({
- ats = [],
- testPlanVersions = [],
- triggerUpdate = () => {}
+ ats = [],
+ testPlanVersions = [],
+ triggerUpdate = () => {}
}) => {
- const { loadingMessage } = useTriggerLoad();
-
- const [showManageATs, setShowManageATs] = useState(false);
- const [showAddTestPlans, setShowAddTestPlans] = useState(false);
-
- const onManageAtsClick = () => setShowManageATs(!showManageATs);
- const onAddTestPlansClick = () => setShowAddTestPlans(!showAddTestPlans);
-
- return (
-
- ,
-
- ]}
- onClick={[onManageAtsClick, onAddTestPlansClick]}
- expanded={[showManageATs, showAddTestPlans]}
- stacked
- />
-
- );
+ const { loadingMessage } = useTriggerLoad();
+
+ const [showManageATs, setShowManageATs] = useState(false);
+ const [showAddTestPlans, setShowAddTestPlans] = useState(false);
+
+ const onManageAtsClick = () => setShowManageATs(!showManageATs);
+ const onAddTestPlansClick = () => setShowAddTestPlans(!showAddTestPlans);
+
+ return (
+
+ ,
+
+ ]}
+ onClick={[onManageAtsClick, onAddTestPlansClick]}
+ expanded={[showManageATs, showAddTestPlans]}
+ stacked
+ />
+
+ );
};
ManageTestQueue.propTypes = {
- ats: PropTypes.arrayOf(
+ ats: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ browsers: PropTypes.arrayOf(
PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- browsers: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- })
- ).isRequired
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
})
- ).isRequired,
- testPlanVersions: PropTypes.array,
- triggerUpdate: PropTypes.func
+ ).isRequired
+ })
+ ).isRequired,
+ testPlanVersions: PropTypes.array,
+ triggerUpdate: PropTypes.func
};
export default ManageTestQueue;
diff --git a/client/components/ManageTestQueue/queries.js b/client/components/ManageTestQueue/queries.js
new file mode 100644
index 000000000..ac5cddf93
--- /dev/null
+++ b/client/components/ManageTestQueue/queries.js
@@ -0,0 +1,58 @@
+import { gql } from '@apollo/client';
+
+export const ADD_AT_VERSION_MUTATION = gql`
+ mutation AddAtVersion($atId: ID!, $name: String!, $releasedAt: Timestamp!) {
+ at(id: $atId) {
+ findOrCreateAtVersion(input: { name: $name, releasedAt: $releasedAt }) {
+ id
+ name
+ releasedAt
+ }
+ }
+ }
+`;
+
+export const EDIT_AT_VERSION_MUTATION = gql`
+ mutation EditAtVersion(
+ $atVersionId: ID!
+ $name: String!
+ $releasedAt: Timestamp!
+ ) {
+ atVersion(id: $atVersionId) {
+ updateAtVersion(input: { name: $name, releasedAt: $releasedAt }) {
+ id
+ name
+ releasedAt
+ }
+ }
+ }
+`;
+
+export const DELETE_AT_VERSION_MUTATION = gql`
+ mutation DeleteAtVersion($atVersionId: ID!) {
+ atVersion(id: $atVersionId) {
+ deleteAtVersion {
+ isDeleted
+ failedDueToTestResults {
+ testPlanVersion {
+ id
+ title
+ }
+ # To be used when listing the conflicting results
+ testResult {
+ id
+ }
+ # To be used when providing more details on the conflicting results
+ testPlanReport {
+ at {
+ name
+ }
+ browser {
+ name
+ }
+ }
+ }
+ }
+ }
+ }
+`;
diff --git a/client/components/NotFound/index.jsx b/client/components/NotFound/index.jsx
index 4d66bdfff..dfc7320df 100644
--- a/client/components/NotFound/index.jsx
+++ b/client/components/NotFound/index.jsx
@@ -3,18 +3,18 @@ import { Container } from 'react-bootstrap';
import { Helmet } from 'react-helmet';
const NotFound = () => {
- return (
-
-
- Page Not Found | ARIA-AT
-
-
-
-
Whoops! Page not found
-
-
-
- );
+ return (
+
+
+ Page Not Found | ARIA-AT
+
+
+
+
Whoops! Page not found
+
+
+
+ );
};
export default NotFound;
diff --git a/client/components/Reports/None.js b/client/components/Reports/None.js
index 2b079aeac..61bdc43b8 100644
--- a/client/components/Reports/None.js
+++ b/client/components/Reports/None.js
@@ -2,8 +2,8 @@ import React from 'react';
import styled from '@emotion/styled';
const StyledNone = styled.span`
- font-style: italic;
- color: #727272;
+ font-style: italic;
+ color: #727272;
`;
const none = None ;
diff --git a/client/components/Reports/Report.jsx b/client/components/Reports/Report.jsx
index dfcea1122..911ca5fa8 100644
--- a/client/components/Reports/Report.jsx
+++ b/client/components/Reports/Report.jsx
@@ -8,58 +8,58 @@ import { REPORT_PAGE_QUERY } from './queries';
import './Reports.css';
const Report = () => {
- const { testPlanVersionId } = useParams();
+ const { testPlanVersionId } = useParams();
- const { loading, data, error } = useQuery(REPORT_PAGE_QUERY, {
- variables: { testPlanVersionId: testPlanVersionId },
- fetchPolicy: 'cache-and-network'
- });
+ const { loading, data, error } = useQuery(REPORT_PAGE_QUERY, {
+ variables: { testPlanVersionId: testPlanVersionId },
+ fetchPolicy: 'cache-and-network'
+ });
- if (error) {
- return (
-
- );
- }
-
- if (loading) {
- return (
-
- );
- }
-
- if (!data) return null;
+ if (error) {
+ return (
+
+ );
+ }
+ if (loading) {
return (
-
-
- }
- />
-
- }
- />
- } />
-
+
);
+ }
+
+ if (!data) return null;
+
+ return (
+
+
+ }
+ />
+
+ }
+ />
+ } />
+
+ );
};
export default Report;
diff --git a/client/components/Reports/Reports.css b/client/components/Reports/Reports.css
index 2a3768dbd..f047eca4e 100644
--- a/client/components/Reports/Reports.css
+++ b/client/components/Reports/Reports.css
@@ -1,145 +1,145 @@
.breadcrumb {
- background: none;
- border: 1px solid gainsboro;
- position: relative;
- font-size: 0.9em;
- margin: 0;
+ background: none;
+ border: 1px solid gainsboro;
+ position: relative;
+ font-size: 0.9em;
+ margin: 0;
}
.breadcrumb-item {
- position: relative;
+ position: relative;
}
.breadcrumb-item + .breadcrumb-item {
- padding-left: 1.5rem;
+ padding-left: 1.5rem;
}
.breadcrumb-item + .breadcrumb-item::before {
- content: '';
- position: absolute;
- width: 1px;
- height: 1.6em;
- background: gainsboro;
- display: initial;
- padding: 0;
- top: -0.8em;
- transform: rotate(-20deg);
- left: 0.75em;
+ content: '';
+ position: absolute;
+ width: 1px;
+ height: 1.6em;
+ background: gainsboro;
+ display: initial;
+ padding: 0;
+ top: -0.8em;
+ transform: rotate(-20deg);
+ left: 0.75em;
}
.breadcrumb-item + .breadcrumb-item::after {
- content: '';
- position: absolute;
- width: 1px;
- height: 1.6em;
- background: gainsboro;
- display: initial;
- padding: 0;
- bottom: -0.8em;
- transform: rotate(20deg);
- left: 0.75em;
+ content: '';
+ position: absolute;
+ width: 1px;
+ height: 1.6em;
+ background: gainsboro;
+ display: initial;
+ padding: 0;
+ bottom: -0.8em;
+ transform: rotate(20deg);
+ left: 0.75em;
}
.breadcrumb-item a {
- color: #0b60ab;
+ color: #0b60ab;
}
.breadcrumb-item a:hover {
- color: #0b60ab;
+ color: #0b60ab;
}
.breadcrumb-item.active {
- color: #000000;
+ color: #000000;
}
.current-commit {
- margin-top: 1em;
+ margin-top: 1em;
}
.test-result-heading {
- display: flex;
- justify-content: space-between;
- align-items: flex-end;
- background: #e9f0f8;
- padding: 1.75em 1.75em 1em 1.75em;
- border: 1px solid #b5cfec;
- margin-top: 2em;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ background: #e9f0f8;
+ padding: 1.75em 1.75em 1em 1.75em;
+ border: 1px solid #b5cfec;
+ margin-top: 2em;
}
.test-result-heading h2 {
- margin-bottom: 0;
+ margin-bottom: 0;
}
.test-result-heading h2 {
- margin-top: 0;
+ margin-top: 0;
}
.test-result-buttons {
- margin-bottom: 0.5em;
- margin-left: 0.5rem;
- flex-shrink: 0;
+ margin-bottom: 0.5em;
+ margin-left: 0.5rem;
+ flex-shrink: 0;
}
.test-result-buttons a {
- margin-right: 0.5em;
+ margin-right: 0.5em;
}
.test-result-buttons .btn {
- border-color: #b5cfec;
+ border-color: #b5cfec;
}
.test-result-buttons .btn:hover {
- background: #f6faff;
+ background: #f6faff;
}
.report-results-details {
- width: 75%;
+ width: 75%;
}
.progress {
- height: 1.2rem;
- border-radius: 1rem;
- margin-top: 0.2em;
- border: 1px solid gray;
+ height: 1.2rem;
+ border-radius: 1rem;
+ margin-top: 0.2em;
+ border: 1px solid gray;
}
.progress .progress-bar.bg-info {
- background-color: #0b60ab !important;
+ background-color: #0b60ab !important;
}
.progress.clipped {
- position: relative;
- display: flex;
- overflow: hidden;
+ position: relative;
+ display: flex;
+ overflow: hidden;
}
.progress.clipped .front {
- position: absolute;
- display: flex;
- justify-content: center;
- align-items: center;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- background: #e9ecef;
- color: black;
- /*clip-path: inset(0 0 0 50%);*/
- /*-webkit-clip-path: inset(0 0 0 50%);*/
+ position: absolute;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background: #e9ecef;
+ color: black;
+ /*clip-path: inset(0 0 0 50%);*/
+ /*-webkit-clip-path: inset(0 0 0 50%);*/
}
.progress.clipped .back {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- background: #0b60ab;
- color: white;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ background: #0b60ab;
+ color: white;
}
.test-details {
- display: block;
- font-size: 0.7em;
- font-weight: normal;
- margin-bottom: 0.25em;
+ display: block;
+ font-size: 0.7em;
+ font-weight: normal;
+ margin-bottom: 0.25em;
}
diff --git a/client/components/Reports/Reports.jsx b/client/components/Reports/Reports.jsx
index f46381447..3337fb4a1 100644
--- a/client/components/Reports/Reports.jsx
+++ b/client/components/Reports/Reports.jsx
@@ -6,39 +6,39 @@ import { REPORTS_PAGE_QUERY } from './queries';
import './Reports.css';
const Reports = () => {
- const { loading, data, error } = useQuery(REPORTS_PAGE_QUERY, {
- fetchPolicy: 'cache-and-network'
- });
+ const { loading, data, error } = useQuery(REPORTS_PAGE_QUERY, {
+ fetchPolicy: 'cache-and-network'
+ });
- if (error) {
- return (
-
- );
- }
-
- if (loading) {
- return (
-
- );
- }
-
- if (!data) return null;
+ if (error) {
+ return (
+
+ );
+ }
+ if (loading) {
return (
- testPlanVersion.testPlanReports.length
- )}
- />
+
);
+ }
+
+ if (!data) return null;
+
+ return (
+ testPlanVersion.testPlanReports.length
+ )}
+ />
+ );
};
export default Reports;
diff --git a/client/components/Reports/SummarizeTestPlanReport.jsx b/client/components/Reports/SummarizeTestPlanReport.jsx
index 4b6509ea8..cda558bc9 100644
--- a/client/components/Reports/SummarizeTestPlanReport.jsx
+++ b/client/components/Reports/SummarizeTestPlanReport.jsx
@@ -6,9 +6,9 @@ import { Breadcrumb, Button, Container, Table } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
- faExclamationCircle,
- faExternalLinkAlt,
- faHome
+ faExclamationCircle,
+ faExternalLinkAlt,
+ faHome
} from '@fortawesome/free-solid-svg-icons';
import styled from '@emotion/styled';
import { getMetrics } from 'shared';
@@ -21,449 +21,417 @@ import { Link, Navigate, useLocation, useParams } from 'react-router-dom';
import createIssueLink from '../../utils/createIssueLink';
const ResultsContainer = styled.div`
- padding: 1em 1.75em;
- border-left: 1px solid #dee2e6;
- border-right: 1px solid #dee2e6;
- border-bottom: 1px solid #dee2e6;
- margin-bottom: 0.5em;
+ padding: 1em 1.75em;
+ border-left: 1px solid #dee2e6;
+ border-right: 1px solid #dee2e6;
+ border-bottom: 1px solid #dee2e6;
+ margin-bottom: 0.5em;
`;
const getTestersRunHistory = (
- testPlanReport,
- testId,
- draftTestPlanRuns = []
+ testPlanReport,
+ testId,
+ draftTestPlanRuns = []
) => {
- const { id: testPlanReportId, at, browser } = testPlanReport;
- let lines = [];
+ const { id: testPlanReportId, at, browser } = testPlanReport;
+ let lines = [];
- draftTestPlanRuns.forEach(draftTestPlanRun => {
- const { testPlanReport, testResults, tester } = draftTestPlanRun;
+ draftTestPlanRuns.forEach(draftTestPlanRun => {
+ const { testPlanReport, testResults, tester } = draftTestPlanRun;
- const testResult = testResults.find(item => item.test.id === testId);
- if (testPlanReportId === testPlanReport.id && testResult?.completedAt) {
- lines.push(
-
- Tested with{' '}
-
- {at.name} {testResult.atVersion.name}
- {' '}
- and{' '}
-
- {browser.name} {testResult.browserVersion.name}
- {' '}
- by{' '}
-
-
- {tester.username}
-
- {' '}
- on{' '}
- {convertDateToString(
- testResult.completedAt,
- 'MMMM DD, YYYY'
- )}
- .
-
- );
- }
- });
-
- return (
- item.test.id === testId);
+ if (testPlanReportId === testPlanReport.id && testResult?.completedAt) {
+ lines.push(
+
- {lines}
-
- );
+ Tested with{' '}
+
+ {at.name} {testResult.atVersion.name}
+ {' '}
+ and{' '}
+
+ {browser.name} {testResult.browserVersion.name}
+ {' '}
+ by{' '}
+
+
+ {tester.username}
+
+ {' '}
+ on {convertDateToString(testResult.completedAt, 'MMMM DD, YYYY')}.
+
+ );
+ }
+ });
+
+ return (
+
+ );
};
const SummarizeTestPlanReport = ({ testPlanVersion, testPlanReports }) => {
- const { exampleUrl, designPatternUrl } = testPlanVersion.metadata;
- const location = useLocation();
- const { testPlanReportId } = useParams();
-
- const testPlanReport = testPlanReports.find(
- each => each.id == testPlanReportId
- );
- if (!testPlanReport) return ;
+ const { exampleUrl, designPatternUrl } = testPlanVersion.metadata;
+ const location = useLocation();
+ const { testPlanReportId } = useParams();
- const { at, browser, recommendedAtVersion } = testPlanReport;
- const overallMetrics = getMetrics({ testPlanReport });
+ const testPlanReport = testPlanReports.find(
+ each => each.id == testPlanReportId
+ );
+ if (!testPlanReport) return ;
- // Construct testPlanTarget
- const testPlanTarget = {
- id: `${at.id}${browser.id}`,
- at,
- browser,
- atVersion: recommendedAtVersion
- };
+ const { at, browser, recommendedAtVersion } = testPlanReport;
+ const overallMetrics = getMetrics({ testPlanReport });
- const renderVersionsSummaryTable = () => {
- if (testPlanVersion.phase !== 'RECOMMENDED') return null;
+ // Construct testPlanTarget
+ const testPlanTarget = {
+ id: `${at.id}${browser.id}`,
+ at,
+ browser,
+ atVersion: recommendedAtVersion
+ };
- const title = `${testPlanTarget.at.name} Versions Summary`;
- const testPlanReportsForTarget = testPlanVersion.testPlanReports.filter(
- testPlanReport =>
- testPlanReport.at.id === at.id &&
- testPlanReport.browser.id === browser.id
- );
- testPlanReportsForTarget.sort(
- (a, b) =>
- new Date(b.recommendedAtVersion.releasedAt) -
- new Date(a.recommendedAtVersion.releasedAt)
- );
+ const renderVersionsSummaryTable = () => {
+ if (testPlanVersion.phase !== 'RECOMMENDED') return null;
- return (
- <>
- {title}
-
- The following table displays a summary of data for all
- versions of {testPlanTarget.at.name} that have been tested.
-
-
-
-
- Versions
- Must-Have Behaviors
- Should-Have Behaviors
- May-Have Behaviors
-
-
-
- {testPlanReportsForTarget.map(testPlanReport => {
- const { recommendedAtVersion, metrics } =
- testPlanReport;
- const {
- mustFormatted,
- shouldFormatted,
- mayFormatted
- } = metrics;
+ const title = `${testPlanTarget.at.name} Versions Summary`;
+ const testPlanReportsForTarget = testPlanVersion.testPlanReports.filter(
+ testPlanReport =>
+ testPlanReport.at.id === at.id &&
+ testPlanReport.browser.id === browser.id
+ );
+ testPlanReportsForTarget.sort(
+ (a, b) =>
+ new Date(b.recommendedAtVersion.releasedAt) -
+ new Date(a.recommendedAtVersion.releasedAt)
+ );
- return (
-
-
- {testPlanReportId ===
- testPlanReport.id ? (
- <>{recommendedAtVersion.name}>
- ) : (
-
- {recommendedAtVersion.name}
-
- )}
-
- {mustFormatted || none}
- {shouldFormatted || none}
- {mayFormatted || none}
-
- );
- })}
-
-
- >
- );
- };
+ return (
+ <>
+ {title}
+
+ The following table displays a summary of data for all versions of{' '}
+ {testPlanTarget.at.name} that have been tested.
+
+
+
+
+ Versions
+ Must-Have Behaviors
+ Should-Have Behaviors
+ May-Have Behaviors
+
+
+
+ {testPlanReportsForTarget.map(testPlanReport => {
+ const { recommendedAtVersion, metrics } = testPlanReport;
+ const { mustFormatted, shouldFormatted, mayFormatted } = metrics;
- const renderResultsForTargetTable = () => {
- const title = `Results for ${getTestPlanTargetTitle(testPlanTarget)}`;
- return (
- <>
- {title}
-
-
-
- Test Name
- Must-Have Behaviors
- Should-Have Behaviors
- May-Have Behaviors
-
-
-
- {testPlanReport.finalizedTestResults.map(testResult => {
- const {
- mustFormatted,
- shouldFormatted,
- mayFormatted
- } = getMetrics({
- testResult
- });
- return (
-
-
-
- {testResult.test.title}
-
-
- {mustFormatted || none}
- {shouldFormatted || none}
- {mayFormatted || none}
-
- );
- })}
-
- All Tests
- {overallMetrics.mustFormatted || none}
- {overallMetrics.shouldFormatted || none}
- {overallMetrics.mayFormatted || none}
-
-
-
- >
- );
- };
+ return (
+
+
+ {testPlanReportId === testPlanReport.id ? (
+ <>{recommendedAtVersion.name}>
+ ) : (
+
+ {recommendedAtVersion.name}
+
+ )}
+
+ {mustFormatted || none}
+ {shouldFormatted || none}
+ {mayFormatted || none}
+
+ );
+ })}
+
+
+ >
+ );
+ };
+ const renderResultsForTargetTable = () => {
+ const title = `Results for ${getTestPlanTargetTitle(testPlanTarget)}`;
return (
-
-
-
- {getTestPlanTargetTitle(testPlanTarget)} for
- {getTestPlanVersionTitle(testPlanVersion)} | ARIA-AT Reports
-
-
-
- {getTestPlanVersionTitle(testPlanVersion)} with
- {getTestPlanTargetTitle(testPlanTarget)}
-
-
-
-
-
- AT Interoperability Reports
-
-
-
-
- {getTestPlanVersionTitle(testPlanVersion)}
-
-
-
- {getTestPlanTargetTitle(testPlanTarget)}
-
-
- Introduction
-
- This page shows detailed results for each test, including
- individual commands that the tester entered into the AT, the
- output which was captured from the AT in response, and whether
- that output was deemed passing or failing for each of the
- assertions. The open test button next to each test allows you to
- preview the test in your browser.
-
- Metadata
-
+ <>
+ {title}
+
+
+
+ Test Name
+ Must-Have Behaviors
+ Should-Have Behaviors
+ May-Have Behaviors
+
+
+
+ {testPlanReport.finalizedTestResults.map(testResult => {
+ const { mustFormatted, shouldFormatted, mayFormatted } =
+ getMetrics({
+ testResult
+ });
+ return (
+
+
+
+ {testResult.test.title}
+
+
+ {mustFormatted || none}
+ {shouldFormatted || none}
+ {mayFormatted || none}
+
+ );
+ })}
+
+ All Tests
+ {overallMetrics.mustFormatted || none}
+ {overallMetrics.shouldFormatted || none}
+ {overallMetrics.mayFormatted || none}
+
+
+
+ >
+ );
+ };
- {renderVersionsSummaryTable()}
- {renderResultsForTargetTable()}
+ return (
+
+
+
+ {getTestPlanTargetTitle(testPlanTarget)} for
+ {getTestPlanVersionTitle(testPlanVersion)} | ARIA-AT Reports
+
+
+
+ {getTestPlanVersionTitle(testPlanVersion)} with
+ {getTestPlanTargetTitle(testPlanTarget)}
+
+
+
+
+
+ AT Interoperability Reports
+
+
+
+
+ {getTestPlanVersionTitle(testPlanVersion)}
+
+
+
+ {getTestPlanTargetTitle(testPlanTarget)}
+
+
+ Introduction
+
+ This page shows detailed results for each test, including individual
+ commands that the tester entered into the AT, the output which was
+ captured from the AT in response, and whether that output was deemed
+ passing or failing for each of the assertions. The open test button next
+ to each test allows you to preview the test in your browser.
+
+ Metadata
+
- {testPlanReport.finalizedTestResults.map((testResult, index) => {
- const test = testResult.test;
+ {renderVersionsSummaryTable()}
+ {renderResultsForTargetTable()}
- const reportLink = `https://aria-at.w3.org${location.pathname}#result-${testResult.id}`;
- const issueLink = createIssueLink({
- testPlanTitle: testPlanVersion.title,
- testPlanDirectory: testPlanVersion.testPlan.directory,
- versionString: testPlanVersion.versionString,
- testTitle: test.title,
- testRowNumber: test.rowNumber,
- testSequenceNumber: index + 1,
- testRenderedUrl: test.renderedUrl,
- atName: testPlanReport.at.name,
- atVersionName: testResult.atVersion.name,
- browserName: testPlanReport.browser.name,
- browserVersionName: testResult.browserVersion.name,
- reportLink
- });
+ {testPlanReport.finalizedTestResults.map((testResult, index) => {
+ const test = testResult.test;
+
+ const reportLink = `https://aria-at.w3.org${location.pathname}#result-${testResult.id}`;
+ const issueLink = createIssueLink({
+ testPlanTitle: testPlanVersion.title,
+ testPlanDirectory: testPlanVersion.testPlan.directory,
+ versionString: testPlanVersion.versionString,
+ testTitle: test.title,
+ testRowNumber: test.rowNumber,
+ testSequenceNumber: index + 1,
+ testRenderedUrl: test.renderedUrl,
+ atName: testPlanReport.at.name,
+ atVersionName: testResult.atVersion.name,
+ browserName: testPlanReport.browser.name,
+ browserVersionName: testResult.browserVersion.name,
+ reportLink
+ });
- // TODO: fix renderedUrl
- let modifiedRenderedUrl = test.renderedUrl.replace(
- /.+(?=\/tests)/,
- 'https://aria-at.netlify.app'
- );
+ // TODO: fix renderedUrl
+ let modifiedRenderedUrl = test.renderedUrl.replace(
+ /.+(?=\/tests)/,
+ 'https://aria-at.netlify.app'
+ );
- const { assertionsPassedCount, assertionsFailedCount } =
- getMetrics({ testResult });
+ const { assertionsPassedCount, assertionsFailedCount } = getMetrics({
+ testResult
+ });
- return (
-
-
-
- Test {index + 1}: {test.title} (
- {assertionsPassedCount}
- passed, {assertionsFailedCount} failed)
-
-
-
-
-
- Raise an Issue
-
-
-
- Open Test
-
-
-
+ return (
+
+
+
+ Test {index + 1}: {test.title} (
+ {assertionsPassedCount}
+ passed, {assertionsFailedCount} failed)
+
+
+
+
+
+ Raise an Issue
+
+
+
+ Open Test
+
+
+
-
- Results for each command
- }
- commandHeadingLevel={4}
- />
-
+
+ Results for each command}
+ commandHeadingLevel={4}
+ />
+
-
-
- );
- })}
-
- );
+
+
+ );
+ })}
+
+ );
};
SummarizeTestPlanReport.propTypes = {
- testPlanVersion: PropTypes.object.isRequired,
- testPlanReports: PropTypes.arrayOf(
+ testPlanVersion: PropTypes.object.isRequired,
+ testPlanReports: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ runnableTests: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
+ at: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ browser: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ finalizedTestResults: PropTypes.arrayOf(
PropTypes.shape({
- id: PropTypes.string.isRequired,
- runnableTests: PropTypes.arrayOf(PropTypes.object.isRequired)
- .isRequired,
- at: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }).isRequired,
- browser: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }).isRequired,
- finalizedTestResults: PropTypes.arrayOf(
+ id: PropTypes.string.isRequired,
+ test: PropTypes.shape({
+ title: PropTypes.string.isRequired,
+ renderedUrl: PropTypes.string.isRequired
+ }).isRequired,
+ scenarioResults: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ output: PropTypes.string.isRequired,
+ assertionResults: PropTypes.arrayOf(
PropTypes.shape({
- id: PropTypes.string.isRequired,
- test: PropTypes.shape({
- title: PropTypes.string.isRequired,
- renderedUrl: PropTypes.string.isRequired
- }).isRequired,
- scenarioResults: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- output: PropTypes.string.isRequired,
- assertionResults: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- passed: PropTypes.bool.isRequired,
- assertion: PropTypes.shape({
- text: PropTypes.string.isRequired
- }).isRequired
- }).isRequired
- ).isRequired,
- unexpectedBehaviors: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- text: PropTypes.string.isRequired,
- impact: PropTypes.string.isRequired,
- details: PropTypes.string.isRequired
- }).isRequired
- ).isRequired
- }).isRequired
- ).isRequired
+ id: PropTypes.string.isRequired,
+ passed: PropTypes.bool.isRequired,
+ assertion: PropTypes.shape({
+ text: PropTypes.string.isRequired
+ }).isRequired
}).isRequired
- ).isRequired,
- draftTestPlanRuns: PropTypes.arrayOf(
+ ).isRequired,
+ unexpectedBehaviors: PropTypes.arrayOf(
PropTypes.shape({
- tester: PropTypes.shape({
- username: PropTypes.string.isRequired
- })
- })
- )
+ id: PropTypes.string.isRequired,
+ text: PropTypes.string.isRequired,
+ impact: PropTypes.string.isRequired,
+ details: PropTypes.string.isRequired
+ }).isRequired
+ ).isRequired
+ }).isRequired
+ ).isRequired
}).isRequired
- )
+ ).isRequired,
+ draftTestPlanRuns: PropTypes.arrayOf(
+ PropTypes.shape({
+ tester: PropTypes.shape({
+ username: PropTypes.string.isRequired
+ })
+ })
+ )
+ }).isRequired
+ )
};
export default SummarizeTestPlanReport;
diff --git a/client/components/Reports/SummarizeTestPlanReports.jsx b/client/components/Reports/SummarizeTestPlanReports.jsx
index 64d2ba2f9..c7f97f62d 100644
--- a/client/components/Reports/SummarizeTestPlanReports.jsx
+++ b/client/components/Reports/SummarizeTestPlanReports.jsx
@@ -11,194 +11,168 @@ import { getTestPlanTargetTitle, getTestPlanVersionTitle } from './getTitles';
import ClippedProgressBar from '@components/common/ClippedProgressBar';
const FullHeightContainer = styled(Container)`
- min-height: calc(100vh - 64px);
+ min-height: calc(100vh - 64px);
`;
const PhaseText = styled.span`
- font-size: 12px;
- margin-left: 6px;
- padding: 4px 6px;
- border-radius: 12px;
- overflow: hidden;
- white-space: nowrap;
- color: white;
+ font-size: 12px;
+ margin-left: 6px;
+ padding: 4px 6px;
+ border-radius: 12px;
+ overflow: hidden;
+ white-space: nowrap;
+ color: white;
- &.candidate {
- background: #f87f1b;
- }
+ &.candidate {
+ background: #f87f1b;
+ }
- &.recommended {
- background: #b253f8;
- }
+ &.recommended {
+ background: #b253f8;
+ }
`;
const SummarizeTestPlanReports = ({ testPlanVersions }) => {
- if (!testPlanVersions.length) {
- return (
-
-
- AT Interop Reports | ARIA-AT
-
- Assistive Technology Interoperability Reports
-
- There are no results to show just yet. Please check back
- soon!
-
-
- );
- }
+ if (!testPlanVersions.length) {
+ return (
+
+
+ AT Interop Reports | ARIA-AT
+
+ Assistive Technology Interoperability Reports
+ There are no results to show just yet. Please check back soon!
+
+ );
+ }
- let testPlanTargetsById = {};
- testPlanVersions.forEach(testPlanVersion => {
- const { testPlanReports } = testPlanVersion;
+ let testPlanTargetsById = {};
+ testPlanVersions.forEach(testPlanVersion => {
+ const { testPlanReports } = testPlanVersion;
- testPlanReports.forEach(testPlanReport => {
- const { at, browser } = testPlanReport;
- // Construct testPlanTarget
- const testPlanTarget = { id: `${at.id}${browser.id}`, at, browser };
- testPlanTargetsById[testPlanTarget.id] = testPlanTarget;
- });
+ testPlanReports.forEach(testPlanReport => {
+ const { at, browser } = testPlanReport;
+ // Construct testPlanTarget
+ const testPlanTarget = { id: `${at.id}${browser.id}`, at, browser };
+ testPlanTargetsById[testPlanTarget.id] = testPlanTarget;
});
- testPlanTargetsById = alphabetizeObjectBy(testPlanTargetsById, keyValue =>
- getTestPlanTargetTitle(keyValue[1])
- );
+ });
+ testPlanTargetsById = alphabetizeObjectBy(testPlanTargetsById, keyValue =>
+ getTestPlanTargetTitle(keyValue[1])
+ );
- return (
-
-
- AT Interop Reports | ARIA-AT
-
- Assistive Technology Interoperability Reports
- Introduction
-
- This page offers a high-level view of all results which have
- been collected, reviewed and published by the ARIA-AT project.
- Follow a link in the table below to view detailed results.
-
- Support Levels
-
- The percentage of assertions which passed when each Test Plan
- was executed by a given Assistive Technology and Browser.
-
-
-
-
- Test Plan
- {Object.values(testPlanTargetsById).map(
- testPlanTarget => (
-
- {getTestPlanTargetTitle(testPlanTarget)}
-
- )
- )}
-
-
-
- {Object.values(testPlanVersions)
- .sort((a, b) => (a.title < b.title ? -1 : 1))
- .map(testPlanVersion => {
- const phase = testPlanVersion.phase;
- const testPlanReports =
- testPlanVersion.testPlanReports;
+ return (
+
+
+ AT Interop Reports | ARIA-AT
+
+ Assistive Technology Interoperability Reports
+ Introduction
+
+ This page offers a high-level view of all results which have been
+ collected, reviewed and published by the ARIA-AT project. Follow a link
+ in the table below to view detailed results.
+
+ Support Levels
+
+ The percentage of assertions which passed when each Test Plan was
+ executed by a given Assistive Technology and Browser.
+
+
+
+
+ Test Plan
+ {Object.values(testPlanTargetsById).map(testPlanTarget => (
+
+ {getTestPlanTargetTitle(testPlanTarget)}
+
+ ))}
+
+
+
+ {Object.values(testPlanVersions)
+ .sort((a, b) => (a.title < b.title ? -1 : 1))
+ .map(testPlanVersion => {
+ const phase = testPlanVersion.phase;
+ const testPlanReports = testPlanVersion.testPlanReports;
- return (
-
-
-
- {getTestPlanVersionTitle(
- testPlanVersion
- )}
-
-
- {derivePhaseName(phase)}
-
-
- {Object.values(testPlanTargetsById).map(
- testPlanTarget => {
- const testPlanReport =
- testPlanReports.find(
- testPlanReport =>
- testPlanReport.at.id ===
- testPlanTarget.at
- .id &&
- testPlanReport.browser
- .id ===
- testPlanTarget
- .browser.id
- );
+ return (
+
+
+
+ {getTestPlanVersionTitle(testPlanVersion)}
+
+
+ {derivePhaseName(phase)}
+
+
+ {Object.values(testPlanTargetsById).map(testPlanTarget => {
+ const testPlanReport = testPlanReports.find(
+ testPlanReport =>
+ testPlanReport.at.id === testPlanTarget.at.id &&
+ testPlanReport.browser.id === testPlanTarget.browser.id
+ );
- if (!testPlanReport) {
- return (
-
- {none}
-
- );
- }
- const metrics =
- testPlanReport.metrics;
- return (
-
-
-
-
-
- );
- }
- )}
-
- );
- })}
-
-
-
- );
+ if (!testPlanReport) {
+ return (
+
+ {none}
+
+ );
+ }
+ const metrics = testPlanReport.metrics;
+ return (
+
+
+
+
+
+ );
+ })}
+
+ );
+ })}
+
+
+
+ );
};
SummarizeTestPlanReports.propTypes = {
- testPlanVersions: PropTypes.arrayOf(
+ testPlanVersions: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ phase: PropTypes.string.isRequired,
+ gitSha: PropTypes.string,
+ testPlan: PropTypes.shape({
+ directory: PropTypes.string
+ }),
+ metadata: PropTypes.object,
+ testPlanReports: PropTypes.arrayOf(
PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string.isRequired,
- phase: PropTypes.string.isRequired,
- gitSha: PropTypes.string,
- testPlan: PropTypes.shape({
- directory: PropTypes.string
- }),
- metadata: PropTypes.object,
- testPlanReports: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- metrics: PropTypes.object.isRequired,
- at: PropTypes.object.isRequired,
- browser: PropTypes.object.isRequired
- })
- )
+ id: PropTypes.string.isRequired,
+ metrics: PropTypes.object.isRequired,
+ at: PropTypes.object.isRequired,
+ browser: PropTypes.object.isRequired
})
- ).isRequired
+ )
+ })
+ ).isRequired
};
export default SummarizeTestPlanReports;
diff --git a/client/components/Reports/SummarizeTestPlanVersion.jsx b/client/components/Reports/SummarizeTestPlanVersion.jsx
index 8997d6e46..427980839 100644
--- a/client/components/Reports/SummarizeTestPlanVersion.jsx
+++ b/client/components/Reports/SummarizeTestPlanVersion.jsx
@@ -14,215 +14,196 @@ import DisclaimerInfo from '../DisclaimerInfo';
import { convertDateToString } from '../../utils/formatter';
const FullHeightContainer = styled(Container)`
- min-height: calc(100vh - 64px);
+ min-height: calc(100vh - 64px);
`;
const SummarizeTestPlanVersion = ({ testPlanVersion, testPlanReports }) => {
- const { exampleUrl, designPatternUrl } = testPlanVersion.metadata;
+ const { exampleUrl, designPatternUrl } = testPlanVersion.metadata;
- // Sort the test plan reports alphabetically by AT name first, then browser
- const sortedTestPlanReports = testPlanReports.slice().sort((a, b) => {
- const atNameA = a.at.name.toLowerCase();
- const atNameB = b.at.name.toLowerCase();
- const browserNameA = a.browser.name.toLowerCase();
- const browserNameB = b.browser.name.toLowerCase();
+ // Sort the test plan reports alphabetically by AT name first, then browser
+ const sortedTestPlanReports = testPlanReports.slice().sort((a, b) => {
+ const atNameA = a.at.name.toLowerCase();
+ const atNameB = b.at.name.toLowerCase();
+ const browserNameA = a.browser.name.toLowerCase();
+ const browserNameB = b.browser.name.toLowerCase();
- if (atNameA < atNameB) return -1;
- if (atNameA > atNameB) return 1;
+ if (atNameA < atNameB) return -1;
+ if (atNameA > atNameB) return 1;
- if (browserNameA < browserNameB) return -1;
- if (browserNameA > browserNameB) return 1;
+ if (browserNameA < browserNameB) return -1;
+ if (browserNameA > browserNameB) return 1;
- return 0;
- });
+ return 0;
+ });
- return (
-
-
-
- {getTestPlanVersionTitle(testPlanVersion)} | ARIA-AT Reports
-
-
- {getTestPlanVersionTitle(testPlanVersion)}
+ return (
+
+
+
+ {getTestPlanVersionTitle(testPlanVersion)} | ARIA-AT Reports
+
+
+ {getTestPlanVersionTitle(testPlanVersion)}
-
-
-
-
- AT Interoperability Reports
-
-
-
- {getTestPlanVersionTitle(testPlanVersion)}
-
-
- Introduction
+
+
+
+
+ AT Interoperability Reports
+
+
+
+ {getTestPlanVersionTitle(testPlanVersion)}
+
+
+ Introduction
-
-
- This page summarizes the test results for each AT and Browser
- which executed the Test Plan.
-
- Metadata
-
+
+
+ This page summarizes the test results for each AT and Browser which
+ executed the Test Plan.
+
+ Metadata
+
- {sortedTestPlanReports.map(testPlanReport => {
- if (testPlanReport.status === 'DRAFT') return null;
- const overallMetrics = getMetrics({ testPlanReport });
+ {sortedTestPlanReports.map(testPlanReport => {
+ if (testPlanReport.status === 'DRAFT') return null;
+ const overallMetrics = getMetrics({ testPlanReport });
- const { at, browser, recommendedAtVersion } = testPlanReport;
+ const { at, browser, recommendedAtVersion } = testPlanReport;
- // Construct testPlanTarget
- const testPlanTarget = {
- id: `${at.id}${browser.id}`,
- at,
- browser,
- atVersion: recommendedAtVersion
- };
+ // Construct testPlanTarget
+ const testPlanTarget = {
+ id: `${at.id}${browser.id}`,
+ at,
+ browser,
+ atVersion: recommendedAtVersion
+ };
- return (
-
- {getTestPlanTargetTitle(testPlanTarget)}
-
- Report completed on{' '}
- {convertDateToString(
- new Date(testPlanReport.markedFinalAt),
- 'MMMM D, YYYY'
- )}
-
-
-
-
- View Complete Results
-
-
-
+ {getTestPlanTargetTitle(testPlanTarget)}
+
+ Report completed on{' '}
+ {convertDateToString(
+ new Date(testPlanReport.markedFinalAt),
+ 'MMMM D, YYYY'
+ )}
+
+
+
+
+ View Complete Results
+
+
+
+
+
+ Test Name
+ Must-Have Behaviors
+ Should-Have Behaviors
+ May-Have Behaviors
+
+
+
+ {testPlanReport.finalizedTestResults.map(testResult => {
+ const { mustFormatted, shouldFormatted, mayFormatted } =
+ getMetrics({
+ testResult
+ });
+ return (
+
+
+
-
-
- Test Name
- Must-Have Behaviors
- Should-Have Behaviors
- May-Have Behaviors
-
-
-
- {testPlanReport.finalizedTestResults.map(
- testResult => {
- const {
- mustFormatted,
- shouldFormatted,
- mayFormatted
- } = getMetrics({
- testResult
- });
- return (
-
-
-
- {testResult.test.title}
-
-
- {mustFormatted || none}
-
- {shouldFormatted || none}
-
- {mayFormatted || none}
-
- );
- }
- )}
-
- All Tests
-
- {overallMetrics.mustFormatted || none}
-
-
- {overallMetrics.shouldFormatted || none}
-
-
- {overallMetrics.mayFormatted || none}
-
-
-
-
-
- );
- })}
-
- );
+ {testResult.test.title}
+
+
+ {mustFormatted || none}
+ {shouldFormatted || none}
+ {mayFormatted || none}
+
+ );
+ })}
+
+ All Tests
+ {overallMetrics.mustFormatted || none}
+ {overallMetrics.shouldFormatted || none}
+ {overallMetrics.mayFormatted || none}
+
+
+
+
+ );
+ })}
+
+ );
};
SummarizeTestPlanVersion.propTypes = {
- testPlanVersion: PropTypes.shape({
- gitSha: PropTypes.string,
- testPlan: PropTypes.object,
- directory: PropTypes.string,
- versionString: PropTypes.string,
- id: PropTypes.string.isRequired,
- title: PropTypes.string,
- phase: PropTypes.string,
- metadata: PropTypes.shape({
- exampleUrl: PropTypes.string.isRequired,
- designPatternUrl: PropTypes.string
- }).isRequired
- }).isRequired,
- testPlanReports: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- runnableTests: PropTypes.arrayOf(PropTypes.object).isRequired,
- finalizedTestResults: PropTypes.arrayOf(PropTypes.object)
- }).isRequired
- ).isRequired
+ testPlanVersion: PropTypes.shape({
+ gitSha: PropTypes.string,
+ testPlan: PropTypes.object,
+ directory: PropTypes.string,
+ versionString: PropTypes.string,
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string,
+ phase: PropTypes.string,
+ metadata: PropTypes.shape({
+ exampleUrl: PropTypes.string.isRequired,
+ designPatternUrl: PropTypes.string
+ }).isRequired
+ }).isRequired,
+ testPlanReports: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ runnableTests: PropTypes.arrayOf(PropTypes.object).isRequired,
+ finalizedTestResults: PropTypes.arrayOf(PropTypes.object)
+ }).isRequired
+ ).isRequired
};
export default SummarizeTestPlanVersion;
diff --git a/client/components/Reports/getTitles.js b/client/components/Reports/getTitles.js
index 8935badb5..ba133264f 100644
--- a/client/components/Reports/getTitles.js
+++ b/client/components/Reports/getTitles.js
@@ -1,16 +1,16 @@
const getTestPlanVersionTitle = (
- testPlanVersion,
- { includeVersionString = false } = {}
+ testPlanVersion,
+ { includeVersionString = false } = {}
) => {
- let title = testPlanVersion.title || testPlanVersion.testPlan?.directory;
- if (includeVersionString && testPlanVersion.versionString)
- title = `${title} ${testPlanVersion.versionString}`;
- return title;
+ let title = testPlanVersion.title || testPlanVersion.testPlan?.directory;
+ if (includeVersionString && testPlanVersion.versionString)
+ title = `${title} ${testPlanVersion.versionString}`;
+ return title;
};
const getTestPlanTargetTitle = ({ at, browser, atVersion }) => {
- if (!atVersion) return `${at.name} and ${browser.name}`;
- return `${at.name} ${atVersion.name} and ${browser.name}`;
+ if (!atVersion) return `${at.name} and ${browser.name}`;
+ return `${at.name} ${atVersion.name} and ${browser.name}`;
};
export { getTestPlanTargetTitle, getTestPlanVersionTitle };
diff --git a/client/components/Reports/queries.js b/client/components/Reports/queries.js
index 65f0799a6..744992153 100644
--- a/client/components/Reports/queries.js
+++ b/client/components/Reports/queries.js
@@ -1,152 +1,150 @@
import { gql } from '@apollo/client';
export const REPORTS_PAGE_QUERY = gql`
- query ReportsPageQuery {
- testPlanVersions(phases: [CANDIDATE, RECOMMENDED]) {
- id
- title
- phase
- gitSha
- updatedAt
- testPlan {
- directory
- }
- metadata
- testPlanReports(isFinal: true) {
- id
- metrics
- at {
- id
- name
- }
- browser {
- id
- name
- }
- }
+ query ReportsPageQuery {
+ testPlanVersions(phases: [CANDIDATE, RECOMMENDED]) {
+ id
+ title
+ phase
+ gitSha
+ updatedAt
+ testPlan {
+ directory
+ }
+ metadata
+ testPlanReports(isFinal: true) {
+ id
+ metrics
+ at {
+ id
+ name
+ }
+ browser {
+ id
+ name
}
+ }
}
+ }
`;
export const REPORT_PAGE_QUERY = gql`
- query ReportPageQuery($testPlanVersionId: ID) {
- testPlanVersion(id: $testPlanVersionId) {
+ query ReportPageQuery($testPlanVersionId: ID) {
+ testPlanVersion(id: $testPlanVersionId) {
+ id
+ title
+ phase
+ gitSha
+ versionString
+ testPlan {
+ directory
+ }
+ metadata
+ testPlanReports(isFinal: true) {
+ id
+ metrics
+ markedFinalAt
+ at {
+ id
+ name
+ }
+ browser {
+ id
+ name
+ }
+ recommendedAtVersion {
+ id
+ name
+ releasedAt
+ }
+ runnableTests {
+ id
+ title
+ renderedUrl
+ }
+ finalizedTestResults {
+ id
+ test {
id
+ rowNumber
title
- phase
- gitSha
- versionString
- testPlan {
- directory
- }
- metadata
- testPlanReports(isFinal: true) {
+ renderedUrl
+ }
+ scenarioResults {
+ id
+ scenario {
+ commands {
id
- metrics
- markedFinalAt
- at {
- id
- name
- }
- browser {
- id
- name
- }
- recommendedAtVersion {
- id
- name
- releasedAt
- }
- runnableTests {
- id
- title
- renderedUrl
- }
- finalizedTestResults {
- id
- test {
- id
- rowNumber
- title
- renderedUrl
- }
- scenarioResults {
- id
- scenario {
- commands {
- id
- text
- }
- }
- output
- assertionResults {
- id
- assertion {
- text
- phrase
- }
- passed
- }
- mustAssertionResults: assertionResults(priority: MUST) {
- assertion {
- text
- phrase
- }
- passed
- }
- shouldAssertionResults: assertionResults(
- priority: SHOULD
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- mayAssertionResults: assertionResults(priority: MAY) {
- assertion {
- text
- phrase
- }
- passed
- }
- unexpectedBehaviors {
- id
- text
- impact
- details
- }
- }
- atVersion {
- name
- }
- browserVersion {
- name
- }
- }
- draftTestPlanRuns {
- tester {
- username
- }
- testPlanReport {
- id
- }
- testResults {
- test {
- id
- }
- atVersion {
- id
- name
- }
- browserVersion {
- id
- name
- }
- completedAt
- }
- }
+ text
+ }
+ }
+ output
+ assertionResults {
+ id
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ mustAssertionResults: assertionResults(priority: MUST) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ shouldAssertionResults: assertionResults(priority: SHOULD) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ mayAssertionResults: assertionResults(priority: MAY) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ unexpectedBehaviors {
+ id
+ text
+ impact
+ details
+ }
+ }
+ atVersion {
+ name
+ }
+ browserVersion {
+ name
+ }
+ }
+ draftTestPlanRuns {
+ tester {
+ username
+ }
+ testPlanReport {
+ id
+ }
+ testResults {
+ test {
+ id
+ }
+ atVersion {
+ id
+ name
+ }
+ browserVersion {
+ id
+ name
}
+ completedAt
+ }
}
+ }
}
+ }
`;
diff --git a/client/components/ReviewConflicts/ReviewConflicts.jsx b/client/components/ReviewConflicts/ReviewConflicts.jsx
index 1a40cd9fc..c97740dc2 100644
--- a/client/components/ReviewConflicts/ReviewConflicts.jsx
+++ b/client/components/ReviewConflicts/ReviewConflicts.jsx
@@ -4,179 +4,177 @@ import styled from '@emotion/styled';
import TurndownService from 'turndown';
const Wrapper = styled.div`
- & h2 {
- margin-top: 0;
- ${props => props.hideHeadline && `display: none;`}
- }
- & ul li {
- list-style: disc;
- margin: 0 0 0.5em 2em;
- }
+ & h2 {
+ margin-top: 0;
+ ${props => props.hideHeadline && `display: none;`}
+ }
+ & ul li {
+ list-style: disc;
+ margin: 0 0 0.5em 2em;
+ }
`;
const ReviewConflicts = ({
- testPlanReport,
- test,
- hideHeadline = false,
- conflictMarkdownRef = null
+ testPlanReport,
+ test,
+ hideHeadline = false,
+ conflictMarkdownRef = null
}) => {
- const contentRef = useRef();
+ const contentRef = useRef();
- useEffect(() => {
- if (!contentRef.current || !conflictMarkdownRef) return;
- const turndownService = new TurndownService({ headingStyle: 'atx' });
- conflictMarkdownRef.current = turndownService.turndown(
- contentRef.current.outerHTML
- );
- });
-
- const conflicts = testPlanReport.conflicts.filter(
- conflict => conflict.source.test.id === test.id
+ useEffect(() => {
+ if (!contentRef.current || !conflictMarkdownRef) return;
+ const turndownService = new TurndownService({ headingStyle: 'atx' });
+ conflictMarkdownRef.current = turndownService.turndown(
+ contentRef.current.outerHTML
);
+ });
- if (conflicts.length === 0) return null;
+ const conflicts = testPlanReport.conflicts.filter(
+ conflict => conflict.source.test.id === test.id
+ );
- const commandString = scenario => {
- return scenario.commands.map(command => command.text).join(', then ');
- };
+ if (conflicts.length === 0) return null;
- const renderConflict = conflict => {
- const assertion = conflict.source.assertion;
- if (assertion) return renderAssertionResultConflict(conflict);
- return renderUnexpectedBehaviorConflict(conflict);
- };
+ const commandString = scenario => {
+ return scenario.commands.map(command => command.text).join(', then ');
+ };
- const renderAssertionResultConflict = ({
- source: { scenario, assertion },
- conflictingResults
- }) => {
- const results = conflictingResults.map(result => {
- const { testPlanRun, scenarioResult, assertionResult } = result;
- let assertionResultFormatted;
- assertionResultFormatted = assertionResult.passed
- ? 'passing'
- : 'failing';
- return (
-
- Tester {testPlanRun.tester.username} recorded output "
- {scenarioResult.output}" and marked assertion as{' '}
- {assertionResultFormatted}.
-
- );
- });
+ const renderConflict = conflict => {
+ const assertion = conflict.source.assertion;
+ if (assertion) return renderAssertionResultConflict(conflict);
+ return renderUnexpectedBehaviorConflict(conflict);
+ };
- return (
-
-
- Assertion Results for "
- {commandString(scenario)}" Command and "
- {assertion.text}" Assertion
-
-
-
- );
- };
+ const renderAssertionResultConflict = ({
+ source: { scenario, assertion },
+ conflictingResults
+ }) => {
+ const results = conflictingResults.map(result => {
+ const { testPlanRun, scenarioResult, assertionResult } = result;
+ let assertionResultFormatted;
+ assertionResultFormatted = assertionResult.passed ? 'passing' : 'failing';
+ return (
+
+ Tester {testPlanRun.tester.username} recorded output "
+ {scenarioResult.output}" and marked assertion as{' '}
+ {assertionResultFormatted}.
+
+ );
+ });
- const renderUnexpectedBehaviorConflict = ({
- source: { scenario },
- conflictingResults
- }) => {
- const results = conflictingResults.map(result => {
- const { testPlanRun, scenarioResult } = result;
- let resultFormatted;
- if (scenarioResult.unexpectedBehaviors.length) {
- resultFormatted = scenarioResult.unexpectedBehaviors
- .map(({ text, impact, details }) => {
- return `"${text} (Details: ${details}, Impact: ${impact})"`;
- })
- .join(' and ');
- } else {
- resultFormatted = 'no unexpected behavior';
- }
- return (
-
- Tester {testPlanRun.tester.username} recorded output "
- {scenarioResult.output}" and noted {resultFormatted}.
-
- );
- });
+ return (
+
+
+ Assertion Results for "
+ {commandString(scenario)}" Command and "
+ {assertion.text}" Assertion
+
+
+
+ );
+ };
- return (
-
-
- Unexpected Behaviors for "{commandString(scenario)}
- " Command
-
-
-
- );
- };
+ const renderUnexpectedBehaviorConflict = ({
+ source: { scenario },
+ conflictingResults
+ }) => {
+ const results = conflictingResults.map(result => {
+ const { testPlanRun, scenarioResult } = result;
+ let resultFormatted;
+ if (scenarioResult.unexpectedBehaviors.length) {
+ resultFormatted = scenarioResult.unexpectedBehaviors
+ .map(({ text, impact, details }) => {
+ return `"${text} (Details: ${details}, Impact: ${impact})"`;
+ })
+ .join(' and ');
+ } else {
+ resultFormatted = 'no unexpected behavior';
+ }
+ return (
+
+ Tester {testPlanRun.tester.username} recorded output "
+ {scenarioResult.output}" and noted {resultFormatted}.
+
+ );
+ });
return (
-
- Review Conflicts for "{test.title}"
- {conflicts.map(renderConflict)}
-
+
+
+ Unexpected Behaviors for "{commandString(scenario)}
+ " Command
+
+
+
);
+ };
+
+ return (
+
+ Review Conflicts for "{test.title}"
+ {conflicts.map(renderConflict)}
+
+ );
};
ReviewConflicts.propTypes = {
- testPlanReport: PropTypes.shape({
- id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
- conflicts: PropTypes.arrayOf(
- PropTypes.shape({
- source: PropTypes.shape({
- test: PropTypes.shape({
- id: PropTypes.string.isRequired
- }).isRequired,
- scenario: PropTypes.shape({
- id: PropTypes.string.isRequired,
- commands: PropTypes.arrayOf(
- PropTypes.shape({
- text: PropTypes.string.isRequired
- })
- ).isRequired
- }).isRequired,
- assertion: PropTypes.shape({
- id: PropTypes.string.isRequired,
- text: PropTypes.string.isRequired
- })
- }).isRequired,
- conflictingResults: PropTypes.arrayOf(
- PropTypes.shape({
- testPlanRun: PropTypes.shape({
- id: PropTypes.string.isRequired,
- tester: PropTypes.shape({
- username: PropTypes.string.isRequired
- }).isRequired
- }).isRequired,
- scenarioResult: PropTypes.shape({
- output: PropTypes.string.isRequired,
- unexpectedBehaviors: PropTypes.arrayOf(
- PropTypes.shape({
- text: PropTypes.string.isRequired,
- impact: PropTypes.string.isRequired,
- details: PropTypes.string.isRequired
- })
- ).isRequired
- }),
- assertionResult: PropTypes.shape({
- passed: PropTypes.bool.isRequired
- })
- })
- ).isRequired
+ testPlanReport: PropTypes.shape({
+ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ conflicts: PropTypes.arrayOf(
+ PropTypes.shape({
+ source: PropTypes.shape({
+ test: PropTypes.shape({
+ id: PropTypes.string.isRequired
+ }).isRequired,
+ scenario: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ commands: PropTypes.arrayOf(
+ PropTypes.shape({
+ text: PropTypes.string.isRequired
+ })
+ ).isRequired
+ }).isRequired,
+ assertion: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ text: PropTypes.string.isRequired
+ })
+ }).isRequired,
+ conflictingResults: PropTypes.arrayOf(
+ PropTypes.shape({
+ testPlanRun: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ tester: PropTypes.shape({
+ username: PropTypes.string.isRequired
+ }).isRequired
+ }).isRequired,
+ scenarioResult: PropTypes.shape({
+ output: PropTypes.string.isRequired,
+ unexpectedBehaviors: PropTypes.arrayOf(
+ PropTypes.shape({
+ text: PropTypes.string.isRequired,
+ impact: PropTypes.string.isRequired,
+ details: PropTypes.string.isRequired
+ })
+ ).isRequired
+ }),
+ assertionResult: PropTypes.shape({
+ passed: PropTypes.bool.isRequired
})
+ })
).isRequired
- }),
- test: PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string.isRequired,
- rowNumber: PropTypes.number.isRequired
- }),
- hideHeadline: PropTypes.bool,
- conflictMarkdownRef: PropTypes.shape({
- current: PropTypes.string
- })
+ })
+ ).isRequired
+ }),
+ test: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ rowNumber: PropTypes.number.isRequired
+ }),
+ hideHeadline: PropTypes.bool,
+ conflictMarkdownRef: PropTypes.shape({
+ current: PropTypes.string
+ })
};
export default ReviewConflicts;
diff --git a/client/components/SignupInstructions/index.jsx b/client/components/SignupInstructions/index.jsx
index 43a4a04c6..075022c3a 100644
--- a/client/components/SignupInstructions/index.jsx
+++ b/client/components/SignupInstructions/index.jsx
@@ -2,18 +2,18 @@ import React from 'react';
import { Container } from 'react-bootstrap';
const SignupInstructions = () => {
- return (
-
-
- You are not yet registered to participate! Let us know you want
- to help by
-
- opening an issue on GitHub
-
- .
-
-
- );
+ return (
+
+
+ You are not yet registered to participate! Let us know you want to help
+ by
+
+ opening an issue on GitHub
+
+ .
+
+
+ );
};
export default SignupInstructions;
diff --git a/client/components/SkipLink/SkipLink.jsx b/client/components/SkipLink/SkipLink.jsx
index d4e2993f9..9e5636805 100644
--- a/client/components/SkipLink/SkipLink.jsx
+++ b/client/components/SkipLink/SkipLink.jsx
@@ -2,36 +2,36 @@ import React from 'react';
import styled from '@emotion/styled';
const SkipAnchorLink = styled.a`
- left: -999px;
- position: absolute;
- width: 1px;
- height: 1px;
- overflow: hidden;
- z-index: -999;
+ left: -999px;
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+ z-index: -999;
- &:focus,
- &:focus-visible,
- &:active {
- color: #fff;
- background-color: #0b60ab;
- outline: 2px solid #a9d1ff;
- outline-offset: 0;
- left: auto;
- width: initial;
- height: auto;
- overflow: auto;
- margin-top: 120px;
- padding: 5px 10px;
- border-radius: 5px;
- text-align: center;
- font-size: 1em;
- z-index: 999;
- display: inline-block;
- }
+ &:focus,
+ &:focus-visible,
+ &:active {
+ color: #fff;
+ background-color: #0b60ab;
+ outline: 2px solid #a9d1ff;
+ outline-offset: 0;
+ left: auto;
+ width: initial;
+ height: auto;
+ overflow: auto;
+ margin-top: 120px;
+ padding: 5px 10px;
+ border-radius: 5px;
+ text-align: center;
+ font-size: 1em;
+ z-index: 999;
+ display: inline-block;
+ }
`;
const SkipLink = () => {
- return Skip to main content ;
+ return Skip to main content ;
};
export default SkipLink;
diff --git a/client/components/TestPlanReportStatusDialog/TestPlanReportStatusDialog.css b/client/components/TestPlanReportStatusDialog/TestPlanReportStatusDialog.css
index 5e4da75a5..b80b772eb 100644
--- a/client/components/TestPlanReportStatusDialog/TestPlanReportStatusDialog.css
+++ b/client/components/TestPlanReportStatusDialog/TestPlanReportStatusDialog.css
@@ -1,4 +1,4 @@
.test-plan-report-status-dialog {
- max-width: 1200px;
- min-width: max-content;
+ max-width: 1200px;
+ min-width: max-content;
}
diff --git a/client/components/TestPlanReportStatusDialog/WithButton.jsx b/client/components/TestPlanReportStatusDialog/WithButton.jsx
index a82215b88..3c416ff59 100644
--- a/client/components/TestPlanReportStatusDialog/WithButton.jsx
+++ b/client/components/TestPlanReportStatusDialog/WithButton.jsx
@@ -9,148 +9,147 @@ import { TEST_PLAN_REPORT_STATUS_DIALOG_QUERY } from './queries';
import { useQuery } from '@apollo/client';
const TestPlanReportStatusDialogButton = styled(Button)`
- display: flex;
- justify-content: center;
- align-items: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
- padding: 0.5rem;
- font-size: 0.875rem;
+ padding: 0.5rem;
+ font-size: 0.875rem;
- border: none;
- border-radius: 0;
+ border: none;
+ border-radius: 0;
- color: #6a7989;
- background: #f6f8fa;
+ color: #6a7989;
+ background: #f6f8fa;
- margin-top: auto;
+ margin-top: auto;
`;
const TestPlanReportStatusDialogWithButton = ({
- testPlanVersionId,
- triggerUpdate: refetchOther
+ testPlanVersionId,
+ triggerUpdate: refetchOther
}) => {
- const {
- data: { testPlanVersion } = {},
- refetch,
- loading
- } = useQuery(TEST_PLAN_REPORT_STATUS_DIALOG_QUERY, {
- variables: { testPlanVersionId },
- fetchPolicy: 'cache-and-network'
- });
+ const {
+ data: { testPlanVersion } = {},
+ refetch,
+ loading
+ } = useQuery(TEST_PLAN_REPORT_STATUS_DIALOG_QUERY, {
+ variables: { testPlanVersionId },
+ fetchPolicy: 'cache-and-network'
+ });
+
+ const buttonRef = useRef(null);
+
+ const [showDialog, setShowDialog] = useState(false);
+ const { testPlanReportStatuses } = testPlanVersion ?? {};
+
+ const buttonLabel = useMemo(() => {
+ if (!testPlanReportStatuses) return;
+
+ const counts = { completed: 0, inProgress: 0, missing: 0 };
+
+ testPlanReportStatuses.forEach(status => {
+ if (!status.isRequired) return;
- const buttonRef = useRef(null);
-
- const [showDialog, setShowDialog] = useState(false);
- const { testPlanReportStatuses } = testPlanVersion ?? {};
-
- const buttonLabel = useMemo(() => {
- if (!testPlanReportStatuses) return;
-
- const counts = { completed: 0, inProgress: 0, missing: 0 };
-
- testPlanReportStatuses.forEach(status => {
- if (!status.isRequired) return;
-
- const { testPlanReport } = status;
-
- if (testPlanReport) {
- const percentComplete =
- calculatePercentComplete(testPlanReport);
-
- if (percentComplete === 100 && testPlanReport.markedFinalAt) {
- counts.completed += 1;
- } else {
- counts.inProgress += 1;
- }
- } else {
- counts.missing += 1;
- }
- });
-
- if (counts.missing === 0 && counts.inProgress === 0) {
- return (
-
-
- Required Reports Complete
-
- );
- } else if (counts.missing === 0 && counts.inProgress !== 0) {
- return (
-
-
- Required Reports In Progress
-
- );
- } else if (
- counts.missing !== 0 &&
- (counts.completed > 0 || counts.inProgress > 0)
- ) {
- return (
-
-
- Some Required Reports Missing
-
- );
- } else if (
- counts.missing !== 0 &&
- counts.completed === 0 &&
- counts.inProgress === 0
- ) {
- return (
-
-
- Required Reports Not Started
-
- );
+ const { testPlanReport } = status;
+
+ if (testPlanReport) {
+ const percentComplete = calculatePercentComplete(testPlanReport);
+
+ if (percentComplete === 100 && testPlanReport.markedFinalAt) {
+ counts.completed += 1;
} else {
- // Fallback case
- return (
-
-
- Some Reports Complete
-
- );
+ counts.inProgress += 1;
}
- }, [testPlanReportStatuses]);
-
- if (
- loading ||
- !testPlanVersion ||
- !testPlanVersion.phase ||
- (testPlanVersion.phase !== 'DRAFT' &&
- testPlanVersion.phase !== 'CANDIDATE' &&
- testPlanVersion.phase !== 'RECOMMENDED')
+ } else {
+ counts.missing += 1;
+ }
+ });
+
+ if (counts.missing === 0 && counts.inProgress === 0) {
+ return (
+
+
+ Required Reports Complete
+
+ );
+ } else if (counts.missing === 0 && counts.inProgress !== 0) {
+ return (
+
+
+ Required Reports In Progress
+
+ );
+ } else if (
+ counts.missing !== 0 &&
+ (counts.completed > 0 || counts.inProgress > 0)
+ ) {
+ return (
+
+
+ Some Required Reports Missing
+
+ );
+ } else if (
+ counts.missing !== 0 &&
+ counts.completed === 0 &&
+ counts.inProgress === 0
) {
- return;
+ return (
+
+
+ Required Reports Not Started
+
+ );
+ } else {
+ // Fallback case
+ return (
+
+
+ Some Reports Complete
+
+ );
}
-
- return (
- <>
- setShowDialog(true)}
- >
- {buttonLabel}
-
- {
- setShowDialog(false);
- buttonRef.current.focus();
- }}
- triggerUpdate={async () => {
- await refetch();
- if (refetchOther) await refetchOther();
- }}
- />
- >
- );
+ }, [testPlanReportStatuses]);
+
+ if (
+ loading ||
+ !testPlanVersion ||
+ !testPlanVersion.phase ||
+ (testPlanVersion.phase !== 'DRAFT' &&
+ testPlanVersion.phase !== 'CANDIDATE' &&
+ testPlanVersion.phase !== 'RECOMMENDED')
+ ) {
+ return;
+ }
+
+ return (
+ <>
+ setShowDialog(true)}
+ >
+ {buttonLabel}
+
+ {
+ setShowDialog(false);
+ buttonRef.current.focus();
+ }}
+ triggerUpdate={async () => {
+ await refetch();
+ if (refetchOther) await refetchOther();
+ }}
+ />
+ >
+ );
};
TestPlanReportStatusDialogWithButton.propTypes = {
- testPlanVersionId: PropTypes.string.isRequired,
- triggerUpdate: PropTypes.func
+ testPlanVersionId: PropTypes.string.isRequired,
+ triggerUpdate: PropTypes.func
};
export default TestPlanReportStatusDialogWithButton;
diff --git a/client/components/TestPlanReportStatusDialog/index.jsx b/client/components/TestPlanReportStatusDialog/index.jsx
index 344e00281..37d109f0a 100644
--- a/client/components/TestPlanReportStatusDialog/index.jsx
+++ b/client/components/TestPlanReportStatusDialog/index.jsx
@@ -11,179 +11,176 @@ import ReportStatusSummary from '../common/ReportStatusSummary';
import { AtVersion } from '../common/AtBrowserVersion';
const TestPlanReportStatusDialog = ({
- testPlanVersion,
- show,
- handleHide = () => {},
- triggerUpdate = () => {}
+ testPlanVersion,
+ show,
+ handleHide = () => {},
+ triggerUpdate = () => {}
}) => {
- const { data: { me } = {} } = useQuery(ME_QUERY, {
- fetchPolicy: 'cache-and-network'
- });
-
- const auth = evaluateAuth(me);
- const { isSignedIn, isAdmin } = auth;
-
- const { testPlanReportStatuses } = testPlanVersion;
-
- let requiredReports = 0;
-
- const tableRows = testPlanReportStatuses.map(status => {
- const {
- isRequired,
- at,
- browser,
- minimumAtVersion,
- exactAtVersion,
- testPlanReport
- } = status;
-
- if (isRequired) requiredReports += 1;
-
- const key =
- `${at.name}-${browser.name}-` +
- `${minimumAtVersion?.id ?? exactAtVersion?.id}-` +
- `${testPlanReport?.id ?? 'missing'}`;
-
- return (
-
- {isRequired ? 'Yes' : 'No'}
-
-
-
- {browser.name}
-
-
- {isSignedIn && isAdmin && !testPlanReport ? (
-
- ) : null}
-
-
- );
- });
-
- const getContent = () => {
- const phase = testPlanVersion.phase;
-
- const getDescriptions = phase => {
- if (phase === 'DRAFT')
- return [
- 'Review phase',
- 'required to be promoted to the next phase'
- ];
- if (phase === 'CANDIDATE')
- return ['Review phase', 'require reports in this phase'];
- if (phase === 'RECOMMENDED')
- return ['phase', 'require reports in this phase'];
- };
-
- const [reviewDescription, requirementNeedsDescription] =
- getDescriptions(phase);
-
- return (
- <>
- {phase && (
-
- This plan is in the
-
- {/* text-transform: capitalize will not work on all-caps string */}
- {phase[0] + phase.slice(1).toLowerCase()}
-
- {reviewDescription}.
- {requiredReports} AT/browser
- pairs {requirementNeedsDescription}.
-
- )}
-
-
-
-
- Required
- AT
- Browser
- Report Status
-
-
- {tableRows}
-
- >
- );
- };
+ const { data: { me } = {} } = useQuery(ME_QUERY, {
+ fetchPolicy: 'cache-and-network'
+ });
+
+ const auth = evaluateAuth(me);
+ const { isSignedIn, isAdmin } = auth;
+
+ const { testPlanReportStatuses } = testPlanVersion;
+
+ let requiredReports = 0;
+
+ const tableRows = testPlanReportStatuses.map(status => {
+ const {
+ isRequired,
+ at,
+ browser,
+ minimumAtVersion,
+ exactAtVersion,
+ testPlanReport
+ } = status;
- const getTitle = () => (
- <>
- Report Status for the
- {testPlanVersion.title}
- Test Plan
- >
+ if (isRequired) requiredReports += 1;
+
+ const key =
+ `${at.name}-${browser.name}-` +
+ `${minimumAtVersion?.id ?? exactAtVersion?.id}-` +
+ `${testPlanReport?.id ?? 'missing'}`;
+
+ return (
+
+ {isRequired ? 'Yes' : 'No'}
+
+
+
+ {browser.name}
+
+
+ {isSignedIn && isAdmin && !testPlanReport ? (
+
+ ) : null}
+
+
);
+ });
+
+ const getContent = () => {
+ const phase = testPlanVersion.phase;
+
+ const getDescriptions = phase => {
+ if (phase === 'DRAFT')
+ return ['Review phase', 'required to be promoted to the next phase'];
+ if (phase === 'CANDIDATE')
+ return ['Review phase', 'require reports in this phase'];
+ if (phase === 'RECOMMENDED')
+ return ['phase', 'require reports in this phase'];
+ };
+
+ const [reviewDescription, requirementNeedsDescription] =
+ getDescriptions(phase);
return (
-
+ <>
+ {phase && (
+
+ This plan is in the
+
+ {/* text-transform: capitalize will not work on all-caps string */}
+ {phase[0] + phase.slice(1).toLowerCase()}
+
+ {reviewDescription}.
+ {requiredReports} AT/browser
+ pairs {requirementNeedsDescription}.
+
+ )}
+
+
+
+
+ Required
+ AT
+ Browser
+ Report Status
+
+
+ {tableRows}
+
+ >
);
+ };
+
+ const getTitle = () => (
+ <>
+ Report Status for the
+ {testPlanVersion.title}
+ Test Plan
+ >
+ );
+
+ return (
+
+ );
};
TestPlanReportStatusDialog.propTypes = {
- testPlanVersion: PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string.isRequired,
- phase: PropTypes.string.isRequired,
- testPlanReportStatuses: PropTypes.arrayOf(
- PropTypes.shape({
- at: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }).isRequired,
- browser: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }).isRequired,
- minimumAtVersion: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }),
- exactAtVersion: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }),
- testPlanReport: PropTypes.shape({
- id: PropTypes.string.isRequired,
- status: PropTypes.string,
- runnableTests: PropTypes.arrayOf(PropTypes.object),
- finalizedTestResults: PropTypes.arrayOf(PropTypes.object)
- })
- }).isRequired
- ).isRequired
- }).isRequired,
- handleHide: PropTypes.func.isRequired,
- triggerUpdate: PropTypes.func,
- show: PropTypes.bool.isRequired
+ testPlanVersion: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ phase: PropTypes.string.isRequired,
+ testPlanReportStatuses: PropTypes.arrayOf(
+ PropTypes.shape({
+ at: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ browser: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ minimumAtVersion: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }),
+ exactAtVersion: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }),
+ testPlanReport: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ status: PropTypes.string,
+ runnableTests: PropTypes.arrayOf(PropTypes.object),
+ finalizedTestResults: PropTypes.arrayOf(PropTypes.object)
+ })
+ }).isRequired
+ ).isRequired
+ }).isRequired,
+ handleHide: PropTypes.func.isRequired,
+ triggerUpdate: PropTypes.func,
+ show: PropTypes.bool.isRequired
};
export default TestPlanReportStatusDialog;
diff --git a/client/components/TestPlanReportStatusDialog/queries.js b/client/components/TestPlanReportStatusDialog/queries.js
index 80a08ffed..75050f8dd 100644
--- a/client/components/TestPlanReportStatusDialog/queries.js
+++ b/client/components/TestPlanReportStatusDialog/queries.js
@@ -1,75 +1,85 @@
import { gql } from '@apollo/client';
export const TEST_PLAN_REPORT_STATUS_DIALOG_QUERY = gql`
- query TestPlanReportStatusDialog($testPlanVersionId: ID!) {
- testPlanVersion(id: $testPlanVersionId) {
+ query TestPlanReportStatusDialog($testPlanVersionId: ID!) {
+ testPlanVersion(id: $testPlanVersionId) {
+ id
+ title
+ phase
+ gitSha
+ gitMessage
+ updatedAt
+ draftPhaseReachedAt
+ candidatePhaseReachedAt
+ recommendedPhaseTargetDate
+ recommendedPhaseReachedAt
+ testPlan {
+ directory
+ }
+ testPlanReportStatuses {
+ isRequired
+ at {
+ id
+ key
+ name
+ atVersions {
id
- title
- phase
- gitSha
- gitMessage
- updatedAt
- draftPhaseReachedAt
- candidatePhaseReachedAt
- recommendedPhaseTargetDate
- recommendedPhaseReachedAt
- testPlan {
- directory
+ name
+ supportedByAutomation
+ releasedAt
+ }
+ }
+ browser {
+ id
+ key
+ name
+ }
+ minimumAtVersion {
+ id
+ name
+ supportedByAutomation
+ releasedAt
+ }
+ exactAtVersion {
+ id
+ name
+ supportedByAutomation
+ releasedAt
+ }
+ testPlanReport {
+ id
+ metrics
+ isFinal
+ markedFinalAt
+ issues {
+ link
+ isOpen
+ feedbackType
+ }
+ draftTestPlanRuns {
+ tester {
+ username
+ }
+ testPlanReport {
+ id
}
- testPlanReportStatuses {
- isRequired
- at {
- id
- key
- name
- }
- browser {
- id
- key
- name
- }
- minimumAtVersion {
- id
- name
- }
- exactAtVersion {
- id
- name
- }
- testPlanReport {
- id
- metrics
- isFinal
- markedFinalAt
- issues {
- link
- isOpen
- feedbackType
- }
- draftTestPlanRuns {
- tester {
- username
- }
- testPlanReport {
- id
- }
- testResults {
- test {
- id
- }
- atVersion {
- id
- name
- }
- browserVersion {
- id
- name
- }
- completedAt
- }
- }
- }
+ testResults {
+ test {
+ id
+ }
+ atVersion {
+ id
+ name
+ }
+ browserVersion {
+ id
+ name
+ }
+ completedAt
}
+ }
}
+ }
}
+ }
`;
diff --git a/client/components/TestPlanVersionsPage/index.jsx b/client/components/TestPlanVersionsPage/index.jsx
index 25c4a1141..bf5b3c71f 100644
--- a/client/components/TestPlanVersionsPage/index.jsx
+++ b/client/components/TestPlanVersionsPage/index.jsx
@@ -6,9 +6,9 @@ import { useParams } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { Container } from 'react-bootstrap';
import {
- ThemeTable,
- ThemeTableUnavailable,
- ThemeTableHeaderH3 as UnstyledThemeTableHeader
+ ThemeTable,
+ ThemeTableUnavailable,
+ ThemeTableHeaderH3 as UnstyledThemeTableHeader
} from '../common/ThemeTable';
import VersionString from '../common/VersionString';
import PhasePill from '../common/PhasePill';
@@ -16,710 +16,610 @@ import { convertDateToString } from '../../utils/formatter';
import { derivePhaseName } from '../../utils/aria';
import styled from '@emotion/styled';
import {
- faArrowUpRightFromSquare,
- faCodeCommit
+ faArrowUpRightFromSquare,
+ faCodeCommit
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import DisclosureComponentUnstyled from '../common/DisclosureComponent';
import useForceUpdate from '../../hooks/useForceUpdate';
const DisclosureContainer = styled.div`
- .timeline-for-version-table {
- padding: 0.5rem 1rem;
- }
+ .timeline-for-version-table {
+ padding: 0.5rem 1rem;
+ }
`;
const DisclosureComponent = styled(DisclosureComponentUnstyled)`
- h2 {
- font-size: 1.25em;
+ h2 {
+ font-size: 1.25em;
- button {
- font-size: unset;
- font-weight: unset;
- }
+ button {
+ font-size: unset;
+ font-weight: unset;
}
+ }
`;
const NoneText = styled.span`
- font-style: italic;
- color: #6a7989;
+ font-style: italic;
+ color: #6a7989;
`;
const PageCommitHistory = styled.div`
- padding: 1.5rem 0 1.5rem;
+ padding: 1.5rem 0 1.5rem;
`;
const PageUl = styled.ul`
- margin-bottom: 2rem;
+ margin-bottom: 2rem;
- li:not(:last-of-type) {
- margin-bottom: 8px;
- }
+ li:not(:last-of-type) {
+ margin-bottom: 8px;
+ }
`;
const PageSpacer = styled.div`
- height: 3rem;
+ height: 3rem;
`;
const CoveredAtDl = styled.dl`
- margin-bottom: 2rem;
-
- dt {
- margin-bottom: 8px;
- }
- li:not(:last-of-type) {
- margin-bottom: 8px;
- }
+ margin-bottom: 2rem;
+
+ dt {
+ margin-bottom: 8px;
+ }
+ li:not(:last-of-type) {
+ margin-bottom: 8px;
+ }
`;
const ThemeTableHeader = styled(UnstyledThemeTableHeader)`
- margin: 0 !important;
+ margin: 0 !important;
`;
const TestPlanVersionsPage = () => {
- const { testPlanDirectory } = useParams();
+ const { testPlanDirectory } = useParams();
- const { loading, data, error } = useQuery(TEST_PLAN_VERSIONS_PAGE_QUERY, {
- variables: { testPlanDirectory },
- fetchPolicy: 'cache-and-network'
- });
- const forceUpdate = useForceUpdate();
-
- const expandedVersionSections = useRef();
- const toggleVersionSections = useRef();
-
- if (error) {
- return (
-
- );
- }
+ const { loading, data, error } = useQuery(TEST_PLAN_VERSIONS_PAGE_QUERY, {
+ variables: { testPlanDirectory },
+ fetchPolicy: 'cache-and-network'
+ });
+ const forceUpdate = useForceUpdate();
- if (loading) {
- return (
-
- );
- }
+ const expandedVersionSections = useRef();
+ const toggleVersionSections = useRef();
- if (!data) return null;
+ if (error) {
+ return (
+
+ );
+ }
- const getPhaseChangeDate = testPlanVersion => {
- let date;
- switch (testPlanVersion.phase) {
- case 'DRAFT':
- date = testPlanVersion.draftPhaseReachedAt;
- break;
- case 'CANDIDATE':
- date = testPlanVersion.candidatePhaseReachedAt;
- break;
- case 'RECOMMENDED':
- date = testPlanVersion.recommendedPhaseReachedAt;
- break;
- case 'RD':
- date = testPlanVersion.updatedAt;
- break;
- case 'DEPRECATED':
- date = testPlanVersion.deprecatedAt;
- break;
- default:
- throw new Error('Unexpected case');
+ if (loading) {
+ return (
+
+ );
+ }
+
+ if (!data) return null;
+
+ const getPhaseChangeDate = testPlanVersion => {
+ let date;
+ switch (testPlanVersion.phase) {
+ case 'DRAFT':
+ date = testPlanVersion.draftPhaseReachedAt;
+ break;
+ case 'CANDIDATE':
+ date = testPlanVersion.candidatePhaseReachedAt;
+ break;
+ case 'RECOMMENDED':
+ date = testPlanVersion.recommendedPhaseReachedAt;
+ break;
+ case 'RD':
+ date = testPlanVersion.updatedAt;
+ break;
+ case 'DEPRECATED':
+ date = testPlanVersion.deprecatedAt;
+ break;
+ default:
+ throw new Error('Unexpected case');
+ }
+ return convertDateToString(date, 'MMM D, YYYY');
+ };
+
+ const getIconColor = testPlanVersion => {
+ return testPlanVersion.phase === 'DEPRECATED' ||
+ testPlanVersion.phase === 'RD'
+ ? '#818F98'
+ : '#2BA51C';
+ };
+
+ const getEventDate = testPlanVersion => {
+ return convertDateToString(
+ (() => {
+ if (testPlanVersion.deprecatedAt) {
+ return testPlanVersion.deprecatedAt;
}
- return convertDateToString(date, 'MMM D, YYYY');
- };
-
- const getIconColor = testPlanVersion => {
- return testPlanVersion.phase === 'DEPRECATED' ||
- testPlanVersion.phase === 'RD'
- ? '#818F98'
- : '#2BA51C';
- };
-
- const getEventDate = testPlanVersion => {
- return convertDateToString(
- (() => {
- if (testPlanVersion.deprecatedAt) {
- return testPlanVersion.deprecatedAt;
- }
- switch (testPlanVersion.phase) {
- case 'RD':
- return testPlanVersion.updatedAt;
- case 'DRAFT':
- return testPlanVersion.draftPhaseReachedAt;
- case 'CANDIDATE':
- return testPlanVersion.candidatePhaseReachedAt;
- case 'RECOMMENDED':
- return testPlanVersion.recommendedPhaseReachedAt;
- case 'DEPRECATED':
- return testPlanVersion.deprecatedAt;
- }
- })(),
- 'MMM D, YYYY'
- );
- };
-
- const getEventBody = phase => {
- const phasePill = {phase} ;
-
- switch (phase) {
- case 'RD':
- return <>{phasePill} Complete>;
- case 'DRAFT':
- case 'CANDIDATE':
- return <>{phasePill} Review Started>;
- case 'RECOMMENDED':
- return <>{phasePill} Approved>;
- case 'DEPRECATED':
- return <>{phasePill}>;
+ switch (testPlanVersion.phase) {
+ case 'RD':
+ return testPlanVersion.updatedAt;
+ case 'DRAFT':
+ return testPlanVersion.draftPhaseReachedAt;
+ case 'CANDIDATE':
+ return testPlanVersion.candidatePhaseReachedAt;
+ case 'RECOMMENDED':
+ return testPlanVersion.recommendedPhaseReachedAt;
+ case 'DEPRECATED':
+ return testPlanVersion.deprecatedAt;
}
- };
-
- const deriveDeprecatedDuringPhase = testPlanVersion => {
- let derivedPhaseDeprecatedDuring = 'RD';
- if (testPlanVersion.recommendedPhaseReachedAt)
- derivedPhaseDeprecatedDuring = 'RECOMMENDED';
- else if (testPlanVersion.candidatePhaseReachedAt)
- derivedPhaseDeprecatedDuring = 'CANDIDATE';
- else if (testPlanVersion.draftPhaseReachedAt)
- derivedPhaseDeprecatedDuring = 'DRAFT';
-
- return derivedPhaseDeprecatedDuring;
- };
-
- const testPlan = data.testPlan;
-
- // GraphQL results are read only so they need to be cloned before sorting
- const issues = [...testPlan.issues].sort((a, b) => {
- const aCreatedAt = new Date(a.createdAt);
- const bCreatedAt = new Date(b.createdAt);
- return bCreatedAt - aCreatedAt;
+ })(),
+ 'MMM D, YYYY'
+ );
+ };
+
+ const getEventBody = phase => {
+ const phasePill = {phase} ;
+
+ switch (phase) {
+ case 'RD':
+ return <>{phasePill} Complete>;
+ case 'DRAFT':
+ case 'CANDIDATE':
+ return <>{phasePill} Review Started>;
+ case 'RECOMMENDED':
+ return <>{phasePill} Approved>;
+ case 'DEPRECATED':
+ return <>{phasePill}>;
+ }
+ };
+
+ const deriveDeprecatedDuringPhase = testPlanVersion => {
+ let derivedPhaseDeprecatedDuring = 'RD';
+ if (testPlanVersion.recommendedPhaseReachedAt)
+ derivedPhaseDeprecatedDuring = 'RECOMMENDED';
+ else if (testPlanVersion.candidatePhaseReachedAt)
+ derivedPhaseDeprecatedDuring = 'CANDIDATE';
+ else if (testPlanVersion.draftPhaseReachedAt)
+ derivedPhaseDeprecatedDuring = 'DRAFT';
+
+ return derivedPhaseDeprecatedDuring;
+ };
+
+ const testPlan = data.testPlan;
+
+ // GraphQL results are read only so they need to be cloned before sorting
+ const issues = [...testPlan.issues].sort((a, b) => {
+ const aCreatedAt = new Date(a.createdAt);
+ const bCreatedAt = new Date(b.createdAt);
+ return bCreatedAt - aCreatedAt;
+ });
+
+ const ats = data.ats;
+
+ const testPlanVersions = data.testPlan.testPlanVersions
+ .slice()
+ .sort((a, b) => {
+ return new Date(b.updatedAt) - new Date(a.updatedAt);
});
- const ats = data.ats;
-
- const testPlanVersions = data.testPlan.testPlanVersions
- .slice()
- .sort((a, b) => {
- return new Date(b.updatedAt) - new Date(a.updatedAt);
- });
-
- const timelineForAllVersions = [];
-
- testPlanVersions.forEach(testPlanVersion => {
- const event = {
- id: testPlanVersion.id,
- updatedAt: testPlanVersion.updatedAt,
- versionString: testPlanVersion.versionString
- };
- timelineForAllVersions.push({ ...event, phase: 'RD' });
-
- if (testPlanVersion.draftPhaseReachedAt)
- timelineForAllVersions.push({
- ...event,
- phase: 'DRAFT',
- draftPhaseReachedAt: testPlanVersion.draftPhaseReachedAt
- });
- if (testPlanVersion.candidatePhaseReachedAt)
- timelineForAllVersions.push({
- ...event,
- phase: 'CANDIDATE',
- candidatePhaseReachedAt: testPlanVersion.candidatePhaseReachedAt
- });
- if (testPlanVersion.recommendedPhaseReachedAt)
- timelineForAllVersions.push({
- ...event,
- phase: 'RECOMMENDED',
- recommendedPhaseReachedAt:
- testPlanVersion.recommendedPhaseReachedAt
- });
- if (testPlanVersion.deprecatedAt)
- timelineForAllVersions.push({
- ...event,
- phase: 'DEPRECATED',
- deprecatedAt: testPlanVersion.deprecatedAt
- });
- });
+ const timelineForAllVersions = [];
- const phaseOrder = {
- RD: 0,
- DRAFT: 1,
- CANDIDATE: 2,
- RECOMMENDED: 3,
- DEPRECATED: 4
+ testPlanVersions.forEach(testPlanVersion => {
+ const event = {
+ id: testPlanVersion.id,
+ updatedAt: testPlanVersion.updatedAt,
+ versionString: testPlanVersion.versionString
};
-
- timelineForAllVersions.sort((a, b) => {
- const dateA =
- a.recommendedPhaseReachedAt ||
- a.candidatePhaseReachedAt ||
- a.draftPhaseReachedAt ||
- a.deprecatedAt ||
- a.updatedAt;
- const dateB =
- b.recommendedPhaseReachedAt ||
- b.candidatePhaseReachedAt ||
- b.draftPhaseReachedAt ||
- b.deprecatedAt ||
- b.updatedAt;
-
- // If dates are the same, compare phases
- if (dateA === dateB) return phaseOrder[a.phase] - phaseOrder[b.phase];
- return new Date(dateA) - new Date(dateB);
- });
-
- if (!expandedVersionSections.current) {
- expandedVersionSections.current = [];
- toggleVersionSections.current = [];
-
- for (let i = 0; i < testPlanVersions.length; i += 1) {
- expandedVersionSections.current[i] = false;
- toggleVersionSections.current[i] = () => {
- expandedVersionSections.current[i] =
- !expandedVersionSections.current[i];
- forceUpdate();
- };
- }
+ timelineForAllVersions.push({ ...event, phase: 'RD' });
+
+ if (testPlanVersion.draftPhaseReachedAt)
+ timelineForAllVersions.push({
+ ...event,
+ phase: 'DRAFT',
+ draftPhaseReachedAt: testPlanVersion.draftPhaseReachedAt
+ });
+ if (testPlanVersion.candidatePhaseReachedAt)
+ timelineForAllVersions.push({
+ ...event,
+ phase: 'CANDIDATE',
+ candidatePhaseReachedAt: testPlanVersion.candidatePhaseReachedAt
+ });
+ if (testPlanVersion.recommendedPhaseReachedAt)
+ timelineForAllVersions.push({
+ ...event,
+ phase: 'RECOMMENDED',
+ recommendedPhaseReachedAt: testPlanVersion.recommendedPhaseReachedAt
+ });
+ if (testPlanVersion.deprecatedAt)
+ timelineForAllVersions.push({
+ ...event,
+ phase: 'DEPRECATED',
+ deprecatedAt: testPlanVersion.deprecatedAt
+ });
+ });
+
+ const phaseOrder = {
+ RD: 0,
+ DRAFT: 1,
+ CANDIDATE: 2,
+ RECOMMENDED: 3,
+ DEPRECATED: 4
+ };
+
+ timelineForAllVersions.sort((a, b) => {
+ const dateA =
+ a.recommendedPhaseReachedAt ||
+ a.candidatePhaseReachedAt ||
+ a.draftPhaseReachedAt ||
+ a.deprecatedAt ||
+ a.updatedAt;
+ const dateB =
+ b.recommendedPhaseReachedAt ||
+ b.candidatePhaseReachedAt ||
+ b.draftPhaseReachedAt ||
+ b.deprecatedAt ||
+ b.updatedAt;
+
+ // If dates are the same, compare phases
+ if (dateA === dateB) return phaseOrder[a.phase] - phaseOrder[b.phase];
+ return new Date(dateA) - new Date(dateB);
+ });
+
+ if (!expandedVersionSections.current) {
+ expandedVersionSections.current = [];
+ toggleVersionSections.current = [];
+
+ for (let i = 0; i < testPlanVersions.length; i += 1) {
+ expandedVersionSections.current[i] = false;
+ toggleVersionSections.current[i] = () => {
+ expandedVersionSections.current[i] =
+ !expandedVersionSections.current[i];
+ forceUpdate();
+ };
}
-
- return (
-
-
- {testPlan.title} Test Plan Versions | ARIA-AT
-
- {testPlan.title} Test Plan Versions
-
-
-
- Commit History for aria-at/tests/{testPlanDirectory}
-
-
- {!testPlanVersions.length ? null : (
- <>
-
- Version Summary
-
-
+
+ {testPlan.title} Test Plan Versions | ARIA-AT
+
+ {testPlan.title} Test Plan Versions
+
+
+
+ Commit History for aria-at/tests/{testPlanDirectory}
+
+
+ {!testPlanVersions.length ? null : (
+ <>
+
+ Version Summary
+
+
+
+
+ Version
+ Latest Phase
+ Phase Change Date
+
+
+
+ {testPlanVersions.map(testPlanVersion => (
+
+
+
-
-
- Version
- Latest Phase
- Phase Change Date
-
-
-
- {testPlanVersions.map(testPlanVersion => (
-
-
-
- {testPlanVersion.versionString}
-
-
-
- {(() => {
- // Gets the derived phase even if deprecated by checking
- // the known dates on the testPlanVersion object
- const derivedDeprecatedAtPhase =
- deriveDeprecatedDuringPhase(
- testPlanVersion
- );
-
- const phasePill = (
-
- {derivedDeprecatedAtPhase}
-
- );
-
- if (testPlanVersion.deprecatedAt) {
- const deprecatedPill = (
-
- DEPRECATED
-
- );
-
- const draftPill = (
-
- DRAFT
-
- );
-
- if (
- derivedDeprecatedAtPhase ===
- 'RD'
- ) {
- return (
- <>
- {deprecatedPill}
- {` before `}
- {draftPill}
- {` review `}
- >
- );
- }
-
- if (
- derivedDeprecatedAtPhase ===
- 'RECOMMENDED'
- ) {
- return (
- <>
- {deprecatedPill}
- {` after being approved as `}
- {phasePill}
- >
- );
- }
-
- return (
- <>
- {deprecatedPill}
- {` during `}
- {phasePill}
- {` review `}
- >
- );
- }
- return phasePill;
- })()}
-
-
- {getPhaseChangeDate(testPlanVersion)}
-
-
- ))}
-
-
-
- >
- )}
-
- GitHub Issues
-
- {!issues.length ? (
-
- No GitHub Issues
-
- ) : (
-
-
-
- Author
- Issue
- Status
- AT
- Created On
- Closed On
-
-
-
- {issues.map(issue => {
- return (
-
-
-
- {issue.author}
-
-
-
-
- {issue.title}
-
-
- {issue.isOpen ? 'Open' : 'Closed'}
-
- {issue.at?.name ?? 'AT not specified'}
-
-
- {convertDateToString(
- issue.createdAt,
- 'MMM D, YYYY'
- )}
-
-
- {!issue.closedAt ? (
- N/A
- ) : (
- convertDateToString(
- issue.closedAt,
- 'MMM D, YYYY'
- )
- )}
-
-
- );
- })}
-
-
- )}
-
-
- Timeline for All Versions
-
-
-
-
- Date
- Event
-
-
-
- {timelineForAllVersions.map(testPlanVersion => {
- const versionString = (
-
- {testPlanVersion.versionString}
-
+ {testPlanVersion.versionString}
+
+
+
+ {(() => {
+ // Gets the derived phase even if deprecated by checking
+ // the known dates on the testPlanVersion object
+ const derivedDeprecatedAtPhase =
+ deriveDeprecatedDuringPhase(testPlanVersion);
+
+ const phasePill = (
+
+ {derivedDeprecatedAtPhase}
+
+ );
+
+ if (testPlanVersion.deprecatedAt) {
+ const deprecatedPill = (
+ DEPRECATED
);
- const eventBody = getEventBody(testPlanVersion.phase);
-
- return (
-
- {getEventDate(testPlanVersion)}
-
- {versionString} {eventBody}
-
-
+ const draftPill = (
+ DRAFT
);
- })}
-
-
-
- {
- return (
-
-
- {testPlanVersion.versionString}
-
-
-
- {testPlanVersion.phase}
-
- on
- {getEventDate(testPlanVersion)}
-
- );
- })}
- disclosureContainerView={testPlanVersions.map(
- testPlanVersion => {
- const hasFinalReports =
- testPlanVersion.testPlanReports.some(
- testPlanReport => testPlanReport.isFinal
- );
+
+ if (derivedDeprecatedAtPhase === 'RD') {
+ return (
+ <>
+ {deprecatedPill}
+ {` before `}
+ {draftPill}
+ {` review `}
+ >
+ );
+ }
+
+ if (derivedDeprecatedAtPhase === 'RECOMMENDED') {
+ return (
+ <>
+ {deprecatedPill}
+ {` after being approved as `}
+ {phasePill}
+ >
+ );
+ }
return (
-
-
-
-
-
-
- Commit{' '}
- {testPlanVersion.gitSha.substr(
- 0,
- 7
- )}
- : {testPlanVersion.gitMessage}
-
-
-
-
-
- View tests in{' '}
- {testPlanVersion.versionString}
-
-
- {!hasFinalReports ? null : (
-
-
-
- View reports generated from{' '}
- {
- testPlanVersion.versionString
- }
-
-
- )}
-
-
- Covered AT
-
-
- {ats.map(at => (
-
- {at.name}
-
- ))}
-
-
-
-
- Timeline for{' '}
- {testPlanVersion.versionString}
-
-
-
-
- Date
- Event
-
-
-
- {(() => {
- let events = [
- [
- 'RD',
- testPlanVersion.updatedAt
- ],
- [
- 'DRAFT',
- testPlanVersion.draftPhaseReachedAt
- ],
- [
- 'CANDIDATE',
- testPlanVersion.candidatePhaseReachedAt
- ],
- [
- 'RECOMMENDED',
- testPlanVersion.recommendedPhaseReachedAt
- ],
- [
- 'DEPRECATED',
- testPlanVersion.deprecatedAt
- ]
- ]
- .filter(event => event[1])
- .sort((a, b) => {
- const dateSort =
- new Date(a[1]) -
- new Date(b[1]);
- if (dateSort === 0)
- return 1; // maintain order above
- return dateSort;
- });
-
- return events.map(
- ([phase, date]) => (
-
-
- {convertDateToString(
- date,
- 'MMM D, YYYY'
- )}
-
-
- {getEventBody(
- phase
- )}
-
-
- )
- );
- })()}
-
-
-
-
+ <>
+ {deprecatedPill}
+ {` during `}
+ {phasePill}
+ {` review `}
+ >
);
- }
- )}
- onClick={toggleVersionSections.current}
- expanded={expandedVersionSections.current}
- stacked={true}
- headingLevel="2"
- />
-
- );
+ }
+ return phasePill;
+ })()}
+
+ {getPhaseChangeDate(testPlanVersion)}
+
+ ))}
+
+
+
+ >
+ )}
+ GitHub Issues
+ {!issues.length ? (
+
+ No GitHub Issues
+
+ ) : (
+
+
+
+ Author
+ Issue
+ Status
+ AT
+ Created On
+ Closed On
+
+
+
+ {issues.map(issue => {
+ return (
+
+
+
+ {issue.author}
+
+
+
+
+ {issue.title}
+
+
+ {issue.isOpen ? 'Open' : 'Closed'}
+ {issue.at?.name ?? 'AT not specified'}
+ {convertDateToString(issue.createdAt, 'MMM D, YYYY')}
+
+ {!issue.closedAt ? (
+ N/A
+ ) : (
+ convertDateToString(issue.closedAt, 'MMM D, YYYY')
+ )}
+
+
+ );
+ })}
+
+
+ )}
+
+
+ Timeline for All Versions
+
+
+
+
+ Date
+ Event
+
+
+
+ {timelineForAllVersions.map(testPlanVersion => {
+ const versionString = (
+
+ {testPlanVersion.versionString}
+
+ );
+
+ const eventBody = getEventBody(testPlanVersion.phase);
+
+ return (
+
+ {getEventDate(testPlanVersion)}
+
+ {versionString} {eventBody}
+
+
+ );
+ })}
+
+
+
+ {
+ return (
+
+
+ {testPlanVersion.versionString}
+
+
+ {testPlanVersion.phase}
+ on
+ {getEventDate(testPlanVersion)}
+
+ );
+ })}
+ disclosureContainerView={testPlanVersions.map(testPlanVersion => {
+ const hasFinalReports = testPlanVersion.testPlanReports.some(
+ testPlanReport => testPlanReport.isFinal
+ );
+
+ return (
+
+
+
+
+
+
+ Commit {testPlanVersion.gitSha.substr(0, 7)}:{' '}
+ {testPlanVersion.gitMessage}
+
+
+
+
+
+ View tests in {testPlanVersion.versionString}
+
+
+ {!hasFinalReports ? null : (
+
+
+
+ View reports generated from{' '}
+ {testPlanVersion.versionString}
+
+
+ )}
+
+
+ Covered AT
+
+
+ {ats.map(at => (
+ {at.name}
+ ))}
+
+
+
+
+ Timeline for {testPlanVersion.versionString}
+
+
+
+
+ Date
+ Event
+
+
+
+ {(() => {
+ let events = [
+ ['RD', testPlanVersion.updatedAt],
+ ['DRAFT', testPlanVersion.draftPhaseReachedAt],
+ ['CANDIDATE', testPlanVersion.candidatePhaseReachedAt],
+ [
+ 'RECOMMENDED',
+ testPlanVersion.recommendedPhaseReachedAt
+ ],
+ ['DEPRECATED', testPlanVersion.deprecatedAt]
+ ]
+ .filter(event => event[1])
+ .sort((a, b) => {
+ const dateSort = new Date(a[1]) - new Date(b[1]);
+ if (dateSort === 0) return 1; // maintain order above
+ return dateSort;
+ });
+
+ return events.map(([phase, date]) => (
+
+ {convertDateToString(date, 'MMM D, YYYY')}
+ {getEventBody(phase)}
+
+ ));
+ })()}
+
+
+
+
+ );
+ })}
+ onClick={toggleVersionSections.current}
+ expanded={expandedVersionSections.current}
+ stacked={true}
+ headingLevel="2"
+ />
+
+ );
};
export default TestPlanVersionsPage;
diff --git a/client/components/TestPlanVersionsPage/queries.js b/client/components/TestPlanVersionsPage/queries.js
index 10beee366..0fa8d2f57 100644
--- a/client/components/TestPlanVersionsPage/queries.js
+++ b/client/components/TestPlanVersionsPage/queries.js
@@ -1,48 +1,48 @@
import { gql } from '@apollo/client';
export const TEST_PLAN_VERSIONS_PAGE_QUERY = gql`
- query TestPlanVersionsPageQuery($testPlanDirectory: ID!) {
- ats {
- id
- name
+ query TestPlanVersionsPageQuery($testPlanDirectory: ID!) {
+ ats {
+ id
+ name
+ }
+ testPlan(id: $testPlanDirectory) {
+ title
+ issues {
+ author
+ title
+ link
+ feedbackType
+ isOpen
+ createdAt
+ closedAt
+ at {
+ name
}
- testPlan(id: $testPlanDirectory) {
- title
- issues {
- author
- title
- link
- feedbackType
- isOpen
- createdAt
- closedAt
- at {
- name
- }
- }
- testPlanVersions {
- id
- testPlan {
- directory
- }
- phase
- updatedAt
- versionString
- deprecatedAt
- gitSha
- gitMessage
- draftPhaseReachedAt
- candidatePhaseReachedAt
- recommendedPhaseReachedAt
- testPlanReports {
- id
- isFinal
- at {
- name
- }
- }
- metadata
- }
+ }
+ testPlanVersions {
+ id
+ testPlan {
+ directory
+ }
+ phase
+ updatedAt
+ versionString
+ deprecatedAt
+ gitSha
+ gitMessage
+ draftPhaseReachedAt
+ candidatePhaseReachedAt
+ recommendedPhaseReachedAt
+ testPlanReports {
+ id
+ isFinal
+ at {
+ name
+ }
}
+ metadata
+ }
}
+ }
`;
diff --git a/client/components/TestQueue/Actions.jsx b/client/components/TestQueue/Actions.jsx
new file mode 100644
index 000000000..c9ba177cc
--- /dev/null
+++ b/client/components/TestQueue/Actions.jsx
@@ -0,0 +1,338 @@
+import React, { useRef } from 'react';
+import PropTypes from 'prop-types';
+import useConfirmationModal from '../../hooks/useConfirmationModal';
+import { LoadingStatus, useTriggerLoad } from '../common/LoadingStatus';
+import { useApolloClient } from '@apollo/client';
+import styled from '@emotion/styled';
+import { Button, Dropdown, Form } from 'react-bootstrap';
+import BasicModal from '../common/BasicModal';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import {
+ faSquareCheck,
+ faFileImport,
+ faTrashAlt
+} from '@fortawesome/free-solid-svg-icons';
+import {
+ MARK_TEST_PLAN_REPORT_AS_FINAL_MUTATION,
+ REMOVE_TEST_PLAN_REPORT_MUTATION,
+ TEST_QUEUE_PAGE_QUERY
+} from './queries';
+import useForceUpdate from '../../hooks/useForceUpdate';
+import BasicThemedModal from '../common/BasicThemedModal';
+import { evaluateAuth } from '../../utils/evaluateAuth';
+import { TEST_PLAN_REPORT_STATUS_DIALOG_QUERY } from '../TestPlanReportStatusDialog/queries';
+import ManageBotRunDialogWithButton from '@components/ManageBotRunDialog/WithButton';
+
+const ActionContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+`;
+
+const Actions = ({
+ me,
+ testPlan,
+ testPlanReport,
+ testers = [],
+ triggerUpdate = () => {}
+}) => {
+ const primaryRunIdRef = useRef({});
+
+ const { showConfirmationModal, hideConfirmationModal } =
+ useConfirmationModal();
+
+ const { triggerLoad, loadingMessage } = useTriggerLoad();
+
+ const forceUpdate = useForceUpdate();
+
+ const client = useApolloClient();
+
+ const { isAdmin, isTester } = evaluateAuth(me);
+
+ const selfAssignedRun =
+ me &&
+ testPlanReport.draftTestPlanRuns.find(
+ testPlanRun => testPlanRun.tester.id === me.id
+ );
+
+ const nonSelfAssignedRuns = testPlanReport.draftTestPlanRuns
+ .filter(testPlanRun => testPlanRun.tester.id !== me?.id)
+ .sort((a, b) => a.tester.username.localeCompare(b.tester.username));
+
+ const completedAllTests = testPlanReport.draftTestPlanRuns.every(
+ testPlanRun =>
+ testPlanRun.testResultsLength === testPlanReport.runnableTestsLength
+ );
+
+ const assignedBotRun = testPlanReport.draftTestPlanRuns.find(
+ testPlanRun => testPlanRun.tester.isBot
+ );
+
+ const canMarkAsFinal =
+ !assignedBotRun &&
+ !testPlanReport.conflictsLength &&
+ testPlanReport.draftTestPlanRuns.length > 0 &&
+ testPlanReport.draftTestPlanRuns[0].testResultsLength > 0 &&
+ completedAllTests;
+
+ const markAsFinal = () => {
+ const runs = testPlanReport.draftTestPlanRuns;
+
+ primaryRunIdRef.current = runs[0].id;
+
+ const onChangePrimary = event => {
+ const id = event.target.value;
+ primaryRunIdRef.current = id;
+ forceUpdate();
+ };
+
+ const onConfirm = async () => {
+ await triggerLoad(async () => {
+ await client.mutate({
+ mutation: MARK_TEST_PLAN_REPORT_AS_FINAL_MUTATION,
+ refetchQueries: [
+ TEST_QUEUE_PAGE_QUERY,
+ TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
+ ],
+ awaitRefetchQueries: true,
+ variables: {
+ testPlanReportId: testPlanReport.id,
+ primaryTestPlanRunId: primaryRunIdRef.current
+ }
+ });
+ }, 'Marking as Final ...');
+
+ hideConfirmationModal();
+ };
+
+ let title;
+ let content;
+
+ if (runs.length === 1) {
+ title =
+ "Are you sure you want to mark as final with a single tester's results?";
+ content = (
+ <>
+
+ Only {runs[0].tester.username}'s results are included in this
+ report, so their run will be marked as the primary run. Only their
+ output will be displayed on report pages.
+
+
+ Their run being marked as primary may also set the minimum required
+ Assistive Technology Version that can be used for subsequent reports
+ with this Test Plan Version and Assistive Technology combination.
+
+ >
+ );
+ } else {
+ // Multiple testers runs to choose from
+ title = 'Select Primary Test Plan Run';
+ content = (
+ <>
+
+ When a tester's run is marked as primary, it means that their
+ output for collected results will be prioritized and shown on report
+ pages.
+
+
+ A tester's run being marked as primary may also set the minimum
+ required Assistive Technology Version that can be used for
+ subsequent reports with that Test Plan Version and Assistive
+ Technology combination.
+
+
+ {runs.map(run => (
+
+ {run.tester.username}
+
+ ))}
+
+ >
+ );
+ }
+
+ showConfirmationModal(
+
+ );
+ };
+
+ const deleteReport = () => {
+ const onConfirm = async () => {
+ await triggerLoad(async () => {
+ await client.mutate({
+ mutation: REMOVE_TEST_PLAN_REPORT_MUTATION,
+ refetchQueries: [
+ TEST_QUEUE_PAGE_QUERY,
+ TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
+ ],
+ awaitRefetchQueries: true,
+ variables: { testPlanReportId: testPlanReport.id }
+ });
+ }, 'Deleting...');
+
+ hideConfirmationModal();
+
+ setTimeout(() => {
+ const focusTarget =
+ document.querySelector(`h2#${testPlan.directory}`) ??
+ document.querySelector('#main');
+
+ focusTarget.focus();
+ }, 1);
+ };
+
+ showConfirmationModal(
+ hideConfirmationModal()}
+ />
+ );
+ };
+
+ return (
+
+
+ {!isTester && (
+
+ View Tests
+
+ )}
+ {isTester && (
+
+ {selfAssignedRun?.testResultsLength
+ ? 'Continue Testing'
+ : 'Start Testing'}
+
+ )}
+ {isAdmin && (
+
+
+
+ Open run as...
+
+
+ {nonSelfAssignedRuns.map(testPlanRun => (
+
+ {testPlanRun.tester.username}
+
+ ))}
+
+
+ )}
+ {isAdmin && assignedBotRun && (
+
+ )}
+ {isAdmin && (
+
+
+ Mark as Final
+
+ )}
+ {isAdmin && (
+
+
+ Delete Report
+
+ )}
+
+
+ );
+};
+
+Actions.propTypes = {
+ me: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ username: PropTypes.string.isRequired
+ }),
+ testPlan: PropTypes.shape({
+ directory: PropTypes.string.isRequired
+ }).isRequired,
+ testPlanReport: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ runnableTestsLength: PropTypes.number.isRequired,
+ conflictsLength: PropTypes.number.isRequired,
+ draftTestPlanRuns: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ testResultsLength: PropTypes.number.isRequired,
+ tester: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ username: PropTypes.string.isRequired,
+ isBot: PropTypes.bool.isRequired
+ })
+ })
+ ).isRequired
+ }).isRequired,
+ testers: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ username: PropTypes.string.isRequired,
+ isBot: PropTypes.bool.isRequired,
+ ats: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired
+ })
+ )
+ })
+ ).isRequired,
+ triggerUpdate: PropTypes.func.isRequired
+};
+
+export default Actions;
diff --git a/client/components/TestQueue/AssignTesterDropdown/AssignTesterDropdown.css b/client/components/TestQueue/AssignTesterDropdown/AssignTesterDropdown.css
deleted file mode 100644
index a7dd6a9bf..000000000
--- a/client/components/TestQueue/AssignTesterDropdown/AssignTesterDropdown.css
+++ /dev/null
@@ -1,34 +0,0 @@
-.assignees {
- margin-top: 0.3em;
- font-size: 0.9rem;
- text-align: center;
-}
-
-.assign-tester {
- border-radius: 25px;
- height: 2.5em;
- border-style: dashed;
- padding: 0.4em 0.6em;
-}
-
-.assign-tester .fa-user-plus {
- margin: 0;
-}
-
-.assign-tester.dropdown-toggle:after {
- display: none;
-}
-
-.no-assignees {
- text-align: center;
- line-height: 2;
-}
-
-.not-assigned.human {
- padding-left: 1.5em;
-}
-
-[role='menu'].assign-menu {
- max-height: 200px;
- overflow-y: scroll;
-}
diff --git a/client/components/TestQueue/AssignTesterDropdown/index.jsx b/client/components/TestQueue/AssignTesterDropdown/index.jsx
deleted file mode 100644
index 73a3f6e68..000000000
--- a/client/components/TestQueue/AssignTesterDropdown/index.jsx
+++ /dev/null
@@ -1,247 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Dropdown } from 'react-bootstrap';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import {
- faCheck,
- faChevronDown,
- faRobot,
- faUserPlus
-} from '@fortawesome/free-solid-svg-icons';
-import {
- ASSIGN_TESTER_MUTATION,
- REMOVE_TESTER_MUTATION,
- TEST_PLAN_REPORT_AT_BROWSER_QUERY
-} from '../queries';
-import { useMutation, useQuery } from '@apollo/client';
-import { LoadingStatus, useTriggerLoad } from '../../common/LoadingStatus';
-import { SCHEDULE_COLLECTION_JOB_MUTATION } from '../../AddTestToQueueWithConfirmation/queries';
-import { isSupportedByResponseCollector } from '../../../utils/automation';
-
-import './AssignTesterDropdown.css';
-
-const AssignTesterDropdown = ({
- testPlanReportId,
- testPlanRun,
- draftTestPlanRuns,
- possibleTesters,
- onChange,
- label,
- dropdownAssignTesterButtonRef,
- setAlertMessage = () => {}
-}) => {
- const { triggerLoad, loadingMessage } = useTriggerLoad();
-
- const { data: testPlanReportAtBrowserQuery } = useQuery(
- TEST_PLAN_REPORT_AT_BROWSER_QUERY,
- {
- variables: {
- testPlanReportId: testPlanReportId
- },
- fetchPolicy: 'cache-and-network'
- }
- );
-
- const [removeTester] = useMutation(REMOVE_TESTER_MUTATION);
- const [assignTester] = useMutation(ASSIGN_TESTER_MUTATION);
- const [scheduleCollection] = useMutation(SCHEDULE_COLLECTION_JOB_MUTATION);
-
- const isTesterAssigned = username => {
- if (testPlanRun) {
- return testPlanRun.tester?.username === username;
- } else if (draftTestPlanRuns?.length) {
- return draftTestPlanRuns.some(
- testPlanRun => testPlanRun.tester?.username === username
- );
- } else {
- return false;
- }
- };
-
- const toggleTesterAssign = async username => {
- const testerIsAssigned = isTesterAssigned(username);
- const tester = possibleTesters.find(
- tester => tester.username === username
- );
-
- if (testerIsAssigned) {
- await triggerLoad(async () => {
- await removeTester({
- variables: {
- testReportId: testPlanReportId,
- testerId: tester.id
- }
- });
- }, `Updating Test Plan Assignees. Deleting Test Plan Run for ${tester.username}`);
- } else {
- if (tester.isBot) {
- await triggerLoad(async () => {
- await scheduleCollection({
- variables: {
- testPlanReportId: testPlanReportId
- }
- });
- }, 'Scheduling Collection Job');
- } else {
- await triggerLoad(async () => {
- await assignTester({
- variables: {
- testReportId: testPlanReportId,
- testerId: tester.id,
- testPlanRunId: testPlanRun?.id // if we are assigning to an existing test plan run, pass the id
- }
- });
- }, 'Updating Test Plan Assignees');
- }
- }
- };
-
- const renderLabel = () => {
- if (label) {
- return (
-
- {label}
-
- );
- } else {
- return ;
- }
- };
- const clearAriaLiveRegion = () => {
- setAlertMessage('');
- };
-
- const handleKeyDown = event => {
- const { key } = event;
- if (key.match(/[0-9a-zA-Z]/)) {
- const container = event.target.closest('[role=menu]');
- const matchingMenuItem = Array.from(container.children).find(
- menuItem => {
- return menuItem.innerText
- .trim()
- .toLowerCase()
- .startsWith(key.toLowerCase());
- }
- );
-
- if (matchingMenuItem) {
- matchingMenuItem.focus();
- }
- }
- };
-
- return (
-
-
-
- {renderLabel()}
-
-
- {possibleTesters?.length ? (
- possibleTesters.map(tester => {
- const { username, isBot, ats } = tester;
- const testerIsAssigned = isTesterAssigned(username);
- const classname = [
- testerIsAssigned ? 'assigned' : 'not-assigned',
- isBot ? 'bot' : 'human'
- ].join(' ');
- let icon;
- if (testerIsAssigned) {
- icon = faCheck;
- } else if (isBot) {
- // if our bot doesn't have a link to the at - hide it from the list
- if (
- !ats.find(
- ({ id }) =>
- id ===
- testPlanReportAtBrowserQuery
- ?.testPlanReport.at.id
- )
- ) {
- return null;
- }
-
- const supportedByBot =
- isSupportedByResponseCollector(
- testPlanReportAtBrowserQuery?.testPlanReport
- );
- if (!supportedByBot) {
- return null;
- }
- icon = faRobot;
- }
- return (
- {
- const updatedIsAssigned =
- !testerIsAssigned;
- setAlertMessage(
- `${username} ${
- updatedIsAssigned
- ? 'now checked'
- : `now unchecked. ${tester.username}'s test plan run has been deleted.`
- }`
- );
- setTimeout(clearAriaLiveRegion, 6000);
- await toggleTesterAssign(username);
- await onChange();
- }}
- >
- {icon && }
-
- {`${tester.username}`}
-
-
- );
- })
- ) : (
-
- No testers to assign
-
- )}
-
-
-
- );
-};
-
-AssignTesterDropdown.propTypes = {
- testPlanReportId: PropTypes.string.isRequired,
- possibleTesters: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- username: PropTypes.string.isRequired,
- isBot: PropTypes.bool.isRequired,
- ats: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired
- })
- )
- })
- ).isRequired,
- onChange: PropTypes.func.isRequired,
- testPlanRun: PropTypes.object,
- label: PropTypes.string,
- draftTestPlanRuns: PropTypes.array,
- setAlertMessage: PropTypes.func,
- dropdownAssignTesterButtonRef: PropTypes.object
-};
-
-export default AssignTesterDropdown;
diff --git a/client/components/TestQueue/AssignTesters.jsx b/client/components/TestQueue/AssignTesters.jsx
new file mode 100644
index 000000000..38f50c076
--- /dev/null
+++ b/client/components/TestQueue/AssignTesters.jsx
@@ -0,0 +1,383 @@
+import React, { useRef } from 'react';
+import PropTypes from 'prop-types';
+import styled from '@emotion/styled';
+import { useApolloClient } from '@apollo/client';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faCheck, faRobot, faUser } from '@fortawesome/free-solid-svg-icons';
+import { Button, Dropdown } from 'react-bootstrap';
+import CompletionStatusListItem from './CompletionStatusListItem';
+import useConfirmationModal from '../../hooks/useConfirmationModal';
+import BasicThemedModal from '../common/BasicThemedModal';
+import { LoadingStatus, useTriggerLoad } from '../common/LoadingStatus';
+import { useAriaLiveRegion } from '../providers/AriaLiveRegionProvider';
+import { evaluateAuth } from '../../utils/evaluateAuth';
+import { isSupportedByResponseCollector } from '../../utils/automation';
+import {
+ ASSIGN_TESTER_MUTATION,
+ DELETE_TEST_PLAN_RUN,
+ TEST_QUEUE_PAGE_QUERY
+} from './queries';
+import { SCHEDULE_COLLECTION_JOB_MUTATION } from '../AddTestToQueueWithConfirmation/queries';
+import { TEST_PLAN_REPORT_STATUS_DIALOG_QUERY } from '../TestPlanReportStatusDialog/queries';
+
+const AssignTestersContainer = styled.div`
+ display: flex;
+
+ [role='menu'] {
+ width: 250px;
+ max-height: 200px;
+ overflow-y: scroll;
+ }
+
+ [role='menuitem']:active {
+ background-color: #0b60ab;
+ }
+`;
+
+const AssignTestersDropdownButton = styled(Dropdown.Toggle)`
+ width: min-content !important;
+ margin-right: 0.5rem;
+`;
+
+const AssignedTestersUl = styled.ul`
+ font-weight: normal;
+ padding-top: 0.5rem;
+ text-align: center;
+
+ li:not(:last-of-type) {
+ padding-bottom: 0.25rem;
+ }
+ a,
+ span {
+ font-weight: normal;
+ padding-right: 0.5rem;
+ }
+ em {
+ color: rgb(var(--bs-secondary-rgb));
+ font-style: normal;
+ display: inline-block;
+ }
+`;
+
+const AssignTesters = ({ me, testers, testPlanReport }) => {
+ const { triggerLoad, loadingMessage } = useTriggerLoad();
+
+ const setAlertMessage = useAriaLiveRegion();
+
+ const { showConfirmationModal, hideConfirmationModal } =
+ useConfirmationModal();
+
+ const client = useApolloClient();
+
+ const dropdownButtonRef = useRef();
+ const assignSelfButtonRef = useRef();
+
+ const { isAdmin, isTester } = evaluateAuth(me);
+
+ const isSelfAssigned =
+ me &&
+ testPlanReport.draftTestPlanRuns.some(
+ testPlanRun => testPlanRun.tester.id === me.id
+ );
+
+ const onToggle = isShown => {
+ setTimeout(() => {
+ if (!isShown) return;
+ document
+ .querySelector(
+ `#assign-testers-${testPlanReport.id} [role="menuitemcheckbox"]`
+ )
+ .focus();
+ }, 1);
+ };
+
+ const onKeyDown = event => {
+ const { key } = event;
+ if (key.match(/[0-9a-zA-Z]/)) {
+ const container = event.target.closest('[role=menu]');
+ const matchingMenuItem = Array.from(container.children).find(menuItem => {
+ return menuItem.innerText
+ .trim()
+ .toLowerCase()
+ .startsWith(key.toLowerCase());
+ });
+
+ if (matchingMenuItem) {
+ matchingMenuItem.focus();
+ }
+ }
+ };
+
+ const toggleTesterCommon = async ({
+ testerId,
+ assignedCallback,
+ unassignedCallback
+ }) => {
+ const tester = testers.find(tester => tester.id === testerId);
+
+ const isAssigned = testPlanReport.draftTestPlanRuns.some(
+ testPlanRun => testPlanRun.tester.id === testerId
+ );
+
+ if (isAssigned) {
+ const isSelfAssigned = tester.id === me.id;
+ const testerFormatted = isSelfAssigned ? 'your' : `${tester.username}'s`;
+
+ const onConfirm = async () => {
+ await triggerLoad(async () => {
+ await client.mutate({
+ mutation: DELETE_TEST_PLAN_RUN,
+ refetchQueries: [
+ TEST_QUEUE_PAGE_QUERY,
+ TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
+ ],
+ awaitRefetchQueries: true,
+ variables: {
+ testReportId: testPlanReport.id,
+ testerId: tester.id
+ }
+ });
+ }, 'Deleting...');
+
+ hideConfirmationModal();
+
+ await assignedCallback?.({ tester });
+ };
+
+ showConfirmationModal(
+ hideConfirmationModal()}
+ />
+ );
+
+ return;
+ }
+
+ await triggerLoad(async () => {
+ if (tester.isBot) {
+ await client.mutate({
+ mutation: SCHEDULE_COLLECTION_JOB_MUTATION,
+ refetchQueries: [
+ TEST_QUEUE_PAGE_QUERY,
+ TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
+ ],
+ awaitRefetchQueries: true,
+ variables: {
+ testPlanReportId: testPlanReport.id
+ }
+ });
+ } else {
+ await client.mutate({
+ mutation: ASSIGN_TESTER_MUTATION,
+ refetchQueries: [
+ TEST_QUEUE_PAGE_QUERY,
+ TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
+ ],
+ awaitRefetchQueries: true,
+ variables: {
+ testReportId: testPlanReport.id,
+ testerId: tester.id
+ }
+ });
+ }
+ }, 'Assigning...');
+
+ await unassignedCallback?.({ tester });
+ };
+
+ const onSelect = testerId => {
+ const assignedCallback = ({ tester }) => {
+ setAlertMessage(`${tester.username}'s run has been deleted`);
+
+ setTimeout(() => {
+ dropdownButtonRef.current.focus();
+ }, 1);
+ };
+
+ const unassignedCallback = ({ tester }) => {
+ setAlertMessage(`Assigned ${tester.username}`);
+
+ setTimeout(() => {
+ dropdownButtonRef.current.focus();
+ }, 1);
+ };
+
+ toggleTesterCommon({ testerId, assignedCallback, unassignedCallback });
+ };
+
+ const onToggleSelf = () => {
+ const assignedCallback = () => {
+ setTimeout(() => {
+ assignSelfButtonRef.current.focus();
+ }, 1);
+ };
+
+ toggleTesterCommon({
+ testerId: me.id,
+ assignedCallback
+ });
+ };
+
+ const renderDropdownItem = ({ tester }) => {
+ const { id, username, isBot, ats } = tester;
+
+ if (isBot) {
+ const foundAtForBot = ats.find(({ id }) => id === testPlanReport.at?.id);
+ const supportedByResponseCollector = isSupportedByResponseCollector({
+ id: testPlanReport.id,
+ at: testPlanReport.at,
+ browser: testPlanReport.browser,
+ minimumAtVersion: testPlanReport.minimumAtVersion,
+ exactAtVersion: testPlanReport.exactAtVersion
+ });
+ if (!foundAtForBot || !supportedByResponseCollector) return null;
+ }
+
+ const isAssigned = testPlanReport.draftTestPlanRuns.some(
+ testPlanRun => testPlanRun.tester.username === username
+ );
+
+ let icon;
+ if (isAssigned) icon = faCheck;
+ else if (isBot) icon = faRobot;
+
+ return (
+
+
+ {icon && }
+ {`${username}`}
+
+
+ );
+ };
+
+ return (
+
+
+ {isAdmin && (
+
+
+ Assign Testers
+
+
+
+ {testers.map(tester => renderDropdownItem({ tester }))}
+
+
+ )}
+ {isTester && (
+
+ {isSelfAssigned ? 'Unassign Yourself' : 'Assign Yourself'}
+
+ )}
+
+
+ {testPlanReport.draftTestPlanRuns
+ .slice()
+ .sort((a, b) => a.tester.username.localeCompare(b.tester.username))
+ .map((testPlanRun, index) => {
+ const tester = testPlanRun.tester;
+ const rowId = `plan-${testPlanReport.id}-assignee-${tester.id}-run-${testPlanRun.id}`;
+ return (
+
+
+ {index ===
+ testPlanReport.draftTestPlanRuns.length - 1 ? null : (
+
+ )}
+
+ );
+ })}
+
+
+ );
+};
+
+AssignTesters.propTypes = {
+ me: PropTypes.shape({
+ id: PropTypes.string.isRequired
+ }),
+ testers: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ username: PropTypes.string.isRequired,
+ isBot: PropTypes.bool.isRequired,
+ ats: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired
+ })
+ )
+ })
+ ).isRequired,
+ testPlanReport: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ runnableTestsLength: PropTypes.number.isRequired,
+ draftTestPlanRuns: PropTypes.arrayOf(
+ PropTypes.shape({
+ tester: PropTypes.shape({ id: PropTypes.string.isRequired }).isRequired
+ })
+ ).isRequired,
+ at: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired,
+ atVersions: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ supportedByAutomation: PropTypes.bool.isRequired
+ })
+ )
+ }).isRequired,
+ browser: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired
+ }).isRequired,
+ minimumAtVersion: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }),
+ exactAtVersion: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ })
+ }).isRequired
+};
+
+export default AssignTesters;
diff --git a/client/components/TestQueue/CompletionStatusListItem/index.jsx b/client/components/TestQueue/CompletionStatusListItem/index.jsx
new file mode 100644
index 000000000..98ed53eaa
--- /dev/null
+++ b/client/components/TestQueue/CompletionStatusListItem/index.jsx
@@ -0,0 +1,86 @@
+import React, { useMemo } from 'react';
+import PropTypes from 'prop-types';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faRobot } from '@fortawesome/free-solid-svg-icons';
+import BotTestCompletionStatus from '@components/TestQueueCompletionStatusListItem/BotTestCompletionStatus';
+import PreviouslyAutomatedTestCompletionStatus from '@components/TestQueueCompletionStatusListItem/PreviouslyAutomatedTestCompletionStatus';
+
+const CompletionStatusListItem = ({
+ rowId,
+ testPlanReport,
+ testPlanRun,
+ tester
+}) => {
+ const { username, isBot } = tester;
+ const testPlanRunPreviouslyAutomated = useMemo(
+ () => testPlanRun.initiatedByAutomation,
+ [testPlanRun]
+ );
+
+ let info;
+ let completionStatus;
+
+ if (isBot) {
+ info = (
+
+
+ {username}
+
+ );
+ completionStatus = (
+
+ );
+ } else {
+ info = (
+
+ {username}
+
+ );
+
+ completionStatus = testPlanRunPreviouslyAutomated ? (
+
+ ) : (
+
+ {`${testPlanRun.testResultsLength} of ` +
+ `${testPlanReport.runnableTestsLength} tests complete`}
+
+ );
+ }
+
+ return (
+
+ {info}
+ {completionStatus}
+
+ );
+};
+
+// TODO: Update shape for testPlanReport and tester
+CompletionStatusListItem.propTypes = {
+ rowId: PropTypes.string.isRequired,
+ testPlanReport: PropTypes.object.isRequired,
+ testPlanRun: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ testResultsLength: PropTypes.number.isRequired,
+ initiatedByAutomation: PropTypes.bool.isRequired,
+ tester: PropTypes.shape({
+ username: PropTypes.string.isRequired,
+ isBot: PropTypes.bool.isRequired
+ }).isRequired
+ }).isRequired,
+ tester: PropTypes.object.isRequired
+};
+
+export default CompletionStatusListItem;
diff --git a/client/components/TestQueue/TestQueue.css b/client/components/TestQueue/TestQueue.css
index 03d9ca34d..9a895e637 100644
--- a/client/components/TestQueue/TestQueue.css
+++ b/client/components/TestQueue/TestQueue.css
@@ -1,97 +1,188 @@
+/* The following is originally from TestQueue.css */
main.container-fluid table .row,
main.container-fluid table .col-md-9,
main.container-fluid table .col-md-3 {
- padding: 0;
+ padding: 0;
}
.test-queue.table td {
- padding: 0;
- /* background: #f4f4f4; */
+ padding: 0;
+ /* background: #f4f4f4; */
}
.test-queue.table .testers-wrapper,
.test-queue.table .status-wrapper,
.test-queue.table .test-cta-wrapper {
- min-height: 3.75rem;
- padding: 0.75em;
- /* background: #ffffff; */
+ min-height: 3.75rem;
+ padding: 0.75em;
+ /* background: #ffffff; */
}
.test-queue.table .test-cta-wrapper :nth-child(n + 2) {
- margin-top: 0.375rem;
+ margin-top: 0.375rem;
}
.test-queue.table .secondary-actions {
- border-top: 1px solid #d2d5d9;
- padding: 0.75em;
+ border-top: 1px solid #d2d5d9;
+ padding: 0.75em;
}
.test-queue.table .secondary-actions .btn {
- padding: 0.375em;
+ padding: 0.375em;
}
.test-queue.table .test-cta-wrapper .btn-primary {
- margin-bottom: 1px;
+ margin-bottom: 1px;
}
.test-queue.table .secondary-actions .btn-danger {
- margin-top: 0.75em;
+ margin-top: 0.75em;
}
.test-queue.table th {
- padding: 0.75rem;
+ padding: 0.75rem;
}
.test-queue.table th.testers {
- width: 225px;
+ width: 225px;
}
.test-queue.table th.report-status,
.test-queue.table th.actions {
- width: 175px;
+ width: 175px;
}
.actions .primary-buttons .open-run-as .btn {
- padding-right: 2em;
- position: relative;
+ padding-right: 2em;
+ position: relative;
}
table .btn {
- width: 100%;
+ width: 100%;
}
table button {
- margin: 0;
+ margin: 0;
}
.add-test-plan-queue-container {
- display: flex;
- align-items: center;
- justify-content: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
- height: 5rem;
+ height: 5rem;
- background: #e9ebee;
- border: 1px solid #d2d5d9;
+ background: #e9ebee;
+ border: 1px solid #d2d5d9;
}
.add-test-plan-queue-button {
- padding-left: 1.5rem;
- padding-right: 1.5rem;
+ padding-left: 1.5rem;
+ padding-right: 1.5rem;
}
.add-test-plan-queue-modal-container {
- display: flex;
- flex-direction: column;
+ display: flex;
+ flex-direction: column;
}
.add-test-plan-queue-modal-row {
- display: grid;
- grid-auto-flow: column;
- grid-template-columns: 1fr 1fr;
- grid-gap: 1rem;
+ display: grid;
+ grid-auto-flow: column;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: 1rem;
}
.add-test-plan-queue-modal-normalize-row {
- margin-top: auto;
+ margin-top: auto;
+}
+
+/* The following is originally from TestQueueRow.css */
+.testers-wrapper {
+ display: flex;
+ flex-direction: row;
+ min-height: 3.75rem;
+}
+
+.testers-wrapper div {
+ flex-basis: auto;
+}
+
+.testers-wrapper .dropdown {
+ padding-right: 0.75em;
+}
+
+.testers-wrapper .assign-actions {
+ width: 100%;
+}
+
+.fullname {
+ color: gray;
+ font-size: 0.9em;
+ margin-left: 0.25em;
+}
+
+svg.svg-inline--fa {
+ margin-right: 0.5em;
+}
+
+button.more-actions svg {
+ color: #9fa4aa;
+}
+
+button.more-actions {
+ background: transparent;
+ color: inherit;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ outline: inherit;
+}
+
+button.more-actions:hover svg,
+button.more-actions:focus svg {
+ color: #6d757d;
+}
+
+button.more-actions.btn-primary:hover,
+button.more-actions.btn-primary:focus {
+ background: transparent;
+}
+
+button.more-actions:active {
+ background: #0053ba;
+}
+
+.primary-buttons {
+ width: inherit;
+}
+
+.reports-link {
+ display: block;
+ text-align: center;
+ margin: 0.45em 0 0;
+ font-size: 0.9rem;
+}
+
+.git-string {
+ font-style: italic;
+ font-size: 0.9rem;
+}
+
+.test-plan {
+ font-size: 1.1rem;
+}
+
+.btn.updater-button {
+ width: initial;
+}
+
+.primary-test-run-select {
+ &[size]:not([size='1']) {
+ padding: 0 !important;
+ }
+
+ option {
+ padding: 0.5rem;
+ }
}
diff --git a/client/components/TestQueue/index.jsx b/client/components/TestQueue/index.jsx
index 590c6e737..3cec38396 100644
--- a/client/components/TestQueue/index.jsx
+++ b/client/components/TestQueue/index.jsx
@@ -1,291 +1,427 @@
-import React, { useEffect, useState } from 'react';
-import { useQuery } from '@apollo/client';
-import { Link } from 'react-router-dom';
-import { Container, Table, Alert } from 'react-bootstrap';
-import { Helmet } from 'react-helmet';
-import nextId from 'react-id-generator';
-import TestQueueRow from '../TestQueueRow';
-import ManageTestQueue from '../ManageTestQueue';
-import DeleteTestPlanReportModal from '../DeleteTestPlanReportModal';
-import DeleteResultsModal from '../DeleteResultsModal';
+import React, { Fragment, useRef } from 'react';
+import { useApolloClient, useQuery } from '@apollo/client';
import PageStatus from '../common/PageStatus';
import { TEST_QUEUE_PAGE_QUERY } from './queries';
+import { Alert, Container, Table as BootstrapTable } from 'react-bootstrap';
+import { Helmet } from 'react-helmet';
import { evaluateAuth } from '../../utils/evaluateAuth';
+import ManageTestQueue from '../ManageTestQueue';
+import DisclosureComponentUnstyled from '../common/DisclosureComponent';
+import useForceUpdate from '../../hooks/useForceUpdate';
+import styled from '@emotion/styled';
+import VersionString from '../common/VersionString';
+import PhasePill from '../common/PhasePill';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faArrowUpRightFromSquare } from '@fortawesome/free-solid-svg-icons';
+import TestPlanReportStatusDialogWithButton from '../TestPlanReportStatusDialog/WithButton';
+import ReportStatusSummary from '../common/ReportStatusSummary';
+import { AtVersion, BrowserVersion } from '../common/AtBrowserVersion';
+import { calculatePercentComplete } from '../../utils/calculatePercentComplete';
+import ProgressBar from '../common/ClippedProgressBar';
+import AssignTesters from './AssignTesters';
+import Actions from './Actions';
+import BotRunTestStatusList from '../BotRunTestStatusList';
import './TestQueue.css';
+const DisclosureComponent = styled(DisclosureComponentUnstyled)`
+ h3 {
+ font-size: 1rem;
+
+ button {
+ font-size: unset;
+ font-weight: unset;
+ }
+ }
+
+ [role='region'] {
+ padding: 0;
+ }
+`;
+
+const MetadataContainer = styled.div`
+ display: flex;
+ gap: 1.25em;
+ margin: 0.5rem 1.25rem;
+ align-items: center;
+ min-height: 40px; /* temp because the status dialog button keeps disappearing */
+
+ & button {
+ margin-bottom: 0;
+ margin-top: 0;
+ font-size: 16px;
+ }
+ & button:hover {
+ color: white;
+ }
+ & button,
+ & button:focus {
+ color: #2e2f33;
+ }
+`;
+
+const TableOverflowContainer = styled.div`
+ width: 100%;
+
+ @media (max-width: 1080px) {
+ overflow-x: scroll;
+ }
+`;
+
+const Table = styled(BootstrapTable)`
+ margin-bottom: 0;
+
+ th {
+ padding: 0.75rem;
+ }
+
+ th:first-of-type,
+ td:first-of-type {
+ border-left: none;
+ }
+ th:last-of-type,
+ td:last-of-type {
+ border-right: none;
+ }
+ tr:last-of-type,
+ tr:last-of-type td {
+ border-bottom: none;
+ }
+
+ th:nth-of-type(1),
+ td:nth-of-type(1) {
+ min-width: 220px;
+ }
+ th:nth-of-type(2),
+ td:nth-of-type(2) {
+ min-width: 150px;
+ }
+ th:nth-of-type(3),
+ td:nth-of-type(3) {
+ min-width: 230px;
+ }
+ th:nth-of-type(4),
+ td:nth-of-type(4) {
+ width: 20%;
+ min-width: 125px;
+ }
+ th:nth-of-type(5),
+ td:nth-of-type(5) {
+ width: 20%;
+ min-width: 175px;
+ }
+`;
+
+const StatusContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ text-align: center;
+ color: rgb(var(--bs-secondary-rgb));
+`;
+
const TestQueue = () => {
- const { loading, data, error, refetch } = useQuery(TEST_QUEUE_PAGE_QUERY, {
- fetchPolicy: 'cache-and-network'
+ const client = useApolloClient();
+ const { data, error, refetch } = useQuery(TEST_QUEUE_PAGE_QUERY, {
+ fetchPolicy: 'cache-and-network'
+ });
+
+ const openDisclosuresRef = useRef({});
+ const forceUpdate = useForceUpdate();
+
+ if (error) {
+ return (
+
+ );
+ }
+
+ if (!data) {
+ return (
+
+ );
+ }
+
+ const isSignedIn = !!data.me;
+
+ const { isAdmin } = evaluateAuth(data.me);
+
+ const testPlanVersions = [];
+ data.testPlans.forEach(testPlan => {
+ // testPlan.directory is needed by ManageTestQueue
+ const populatedTestPlanVersions = testPlan.testPlanVersions.map(
+ testPlanVersion => ({
+ ...testPlanVersion,
+ testPlan: { directory: testPlan.directory }
+ })
+ );
+ testPlanVersions.push(...populatedTestPlanVersions);
+ });
+
+ // Remove any test plans or test plan versions without reports and sort
+ const sortTestPlanVersions = testPlanVersions => {
+ return [...testPlanVersions]
+ .filter(testPlanVersion => testPlanVersion.testPlanReports.length)
+ .sort((a, b) => {
+ return b.versionString.localeCompare(a.versionString);
+ })
+ .map(testPlanVersion => {
+ return {
+ ...testPlanVersion,
+ testPlanReports: sortTestPlanReports(testPlanVersion.testPlanReports)
+ };
+ });
+ };
+
+ const sortTestPlanReports = testPlanReports => {
+ return [...testPlanReports].sort((a, b) => {
+ if (a.at.name !== b.at.name) {
+ return a.at.name.localeCompare(b.at.name);
+ }
+ if (a.browser.name !== b.browser.name) {
+ return a.browser.name.localeCompare(b.browser.name);
+ }
+ const dateA = new Date(
+ (a.minimumAtVersion ?? a.exactAtVersion).releasedAt
+ );
+ const dateB = new Date(
+ (a.minimumAtVersion ?? a.exactAtVersion).releasedAt
+ );
+ return dateB - dateA;
});
+ };
+
+ const testPlans = data.testPlans
+ .filter(testPlan => {
+ for (const testPlanVersion of testPlan.testPlanVersions) {
+ if (testPlanVersion.testPlanReports.length) return true;
+ }
+ })
+ .map(testPlan => {
+ return {
+ ...testPlan,
+ testPlanVersions: sortTestPlanVersions(testPlan.testPlanVersions)
+ };
+ })
+ .sort((a, b) => {
+ return a.title.localeCompare(b.title);
+ });
+
+ const testers = data.users
+ .filter(user => user.roles.includes('TESTER'))
+ .sort((a, b) => a.username.localeCompare(b.username));
- const [pageReady, setPageReady] = useState(false);
- const [testers, setTesters] = useState([]);
- const [ats, setAts] = useState([]);
- const [testPlanVersions, setTestPlanVersions] = useState([]);
- const [testPlanReports, setTestPlanReports] = useState([]);
- const [latestTestPlanVersions, setLatestTestPlanVersions] = useState([]);
- const [structuredTestPlanTargets, setStructuredTestPlanTargets] = useState(
- {}
+ const renderDisclosure = ({ testPlan }) => {
+ return (
+ // TODO: fix the aria-label of this
+ (
+ <>
+
+ {testPlanVersion.versionString}
+
+
+ {testPlanVersion.phase}
+ >
+ ))}
+ onClick={testPlan.testPlanVersions.map(testPlanVersion => () => {
+ const isOpen = openDisclosuresRef.current[testPlanVersion.id];
+ openDisclosuresRef.current[testPlanVersion.id] = !isOpen;
+ forceUpdate();
+ })}
+ expanded={testPlan.testPlanVersions.map(
+ testPlanVersion =>
+ openDisclosuresRef.current[testPlanVersion.id] || false
+ )}
+ disclosureContainerView={testPlan.testPlanVersions.map(
+ testPlanVersion =>
+ renderDisclosureContent({ testPlan, testPlanVersion })
+ )}
+ />
);
- const [deleteTestPlanReportDetails, setDeleteTestPlanReportDetails] =
- useState({});
- const [isShowingDeleteTestPlanReportModal, setDeleteTestPlanReportModal] =
- useState(false);
- const [deleteResultsDetails, setDeleteResultsDetails] = useState({});
- const [isShowingDeleteResultsModal, setDeleteResultsModal] =
- useState(false);
-
- const auth = evaluateAuth(data && data.me ? data.me : {});
- const { id, isAdmin } = auth;
- const isSignedIn = !!id;
-
- useEffect(() => {
- if (data) {
- const {
- users = [],
- ats = [],
- testPlanVersions = [],
- testPlanReports = [],
- testPlans = []
- } = data;
- setTesters(
- users.filter(
- tester =>
- tester.roles.includes('TESTER') ||
- tester.roles.includes('ADMIN')
- )
- );
- setAts(ats);
- setTestPlanVersions(testPlanVersions);
- setTestPlanReports(testPlanReports);
- setLatestTestPlanVersions(testPlans);
- setPageReady(true);
- }
- }, [data]);
-
- useEffect(() => {
- const structuredTestPlanTargets =
- generateStructuredTestPlanVersions(testPlanReports);
- setStructuredTestPlanTargets(structuredTestPlanTargets);
- }, [testPlanReports]);
-
- const generateStructuredTestPlanVersions = testPlanReports => {
- const structuredData = {};
-
- // get all testPlanTargets grouped to make it easier to drop into TestQueue table
- testPlanReports.forEach(testPlanReport => {
- const title = `${testPlanReport.at?.name} and ${testPlanReport.browser?.name}`;
- if (!structuredData[title]) {
- structuredData[title] = [testPlanReport];
- } else {
- structuredData[title].push(testPlanReport);
- }
- });
-
- return structuredData;
- };
-
- const renderAtBrowserList = (title = '', testPlanReports = []) => {
- // means structuredTestPlanTargets would have been generated
- if (!testPlanReports.length) return null;
-
- const tableId = nextId('table_name_');
-
- return (
-
-
{title}
-
-
-
- Test Plan
-
- AT Version Requirements
-
- Testers
- Report Status
- Actions
-
-
-
- {testPlanReports.map(testPlanReport => {
- const key = `test_plan_report_${testPlanReport.id}`;
- return (
-
- a.username.localeCompare(b.username)
- )}
- testPlanReportData={testPlanReport}
- latestTestPlanVersions={
- latestTestPlanVersions
- }
- triggerDeleteTestPlanReportModal={
- triggerDeleteTestPlanReportModal
- }
- triggerDeleteResultsModal={
- triggerDeleteResultsModal
- }
- triggerPageUpdate={refetch}
- />
- );
- })}
-
-
-
- );
- };
-
- const triggerDeleteTestPlanReportModal = (
- id = null,
- title = null,
- deleteFunction = () => {}
- ) => {
- setDeleteTestPlanReportDetails({ id, title, deleteFunction });
- setDeleteTestPlanReportModal(true);
- };
-
- const handleDeleteTestPlanReport = async () => {
- handleCloseDeleteTestPlanReportModal();
-
- if (deleteTestPlanReportDetails.deleteFunction)
- await deleteTestPlanReportDetails.deleteFunction();
-
- // reset deleteTestPlanDetails
- setDeleteTestPlanReportDetails({});
- };
-
- const handleCloseDeleteTestPlanReportModal = () =>
- setDeleteTestPlanReportModal(false);
-
- const triggerDeleteResultsModal = (
- title = null,
- username = null,
- deleteFunction = () => {}
- ) => {
- setDeleteResultsDetails({
- title,
- username,
- deleteFunction
- });
- setDeleteResultsModal(true);
- };
-
- const handleDeleteResults = async () => {
- handleCloseDeleteResultsModal();
-
- if (deleteResultsDetails.deleteFunction)
- await deleteResultsDetails.deleteFunction();
-
- // reset deleteResultsDetails
- setDeleteResultsDetails({});
- };
-
- const handleCloseDeleteResultsModal = () => setDeleteResultsModal(false);
-
- if (error) {
- return (
-
- );
- }
+ };
- if (loading || !pageReady) {
- return (
- {
+ return (
+ <>
+
+
+
- );
- }
+ View tests in {testPlanVersion.versionString}
+
+
+
+
+
+
+
+ Assistive Technology
+ Browser
+ Testers
+ Status
+ Actions
+
+
+
+ {testPlanVersion.testPlanReports.map(testPlanReport =>
+ renderRow({
+ testPlan,
+ testPlanVersion,
+ testPlanReport
+ })
+ )}
+
+
+
+ >
+ );
+ };
- const emptyTestPlans = !testPlanReports.length;
- const noTestPlansMessage = 'There are no test plans available';
- const settingsLink = Settings;
+ const renderRow = ({ testPlan, testPlanVersion, testPlanReport }) => {
+ const percentComplete = calculatePercentComplete(testPlanReport);
+ const hasBotRun = testPlanReport.draftTestPlanRuns?.some(
+ ({ tester }) => tester.isBot
+ );
return (
-
-
- {`${
- emptyTestPlans ? noTestPlansMessage : 'Test Queue'
- } | ARIA-AT`}
-
- Test Queue
- {emptyTestPlans && (
-
- {noTestPlansMessage}
-
- )}
- {emptyTestPlans && !isAdmin && isSignedIn && (
-
- Please configure your preferred Assistive Technologies in
- the {settingsLink} page.
-
- )}
-
- {emptyTestPlans && isAdmin && (
-
- Add a Test Plan to the Queue
-
- )}
-
- {!emptyTestPlans && (
-
- {isSignedIn
- ? 'Assign yourself a test plan or start executing one that is already assigned to you.'
- : 'Select a test plan to view. Your results will not be saved.'}
-
- )}
-
- {isAdmin && (
-
- )}
-
- {!emptyTestPlans &&
- Object.keys(structuredTestPlanTargets).map(key =>
- renderAtBrowserList(key, structuredTestPlanTargets[key])
- )}
-
- {isSignedIn && (
-
- )}
-
- {isAdmin && isShowingDeleteTestPlanReportModal && (
-
- )}
-
+
+
+
+
+
+
+
+
+
+
+
+
+ { }
+
+ {hasBotRun ? (
+
+ ) : null}
+
+
+
+ {
+ await client.refetchQueries({
+ include: ['TestQueuePage', 'TestPlanReportStatusDialog']
+ });
+
+ // Refocus on testers assignment dropdown button
+ const selector = `#assign-testers-${testPlanReport.id} button`;
+ document.querySelector(selector).focus();
+ }}
+ />
+
+
);
+ };
+
+ const renderNoReportsMessage = () => {
+ if (isAdmin) {
+ return (
+ <>
+
+ There are currently no test plan reports available.
+
+
+ Add a Test Plan to the Queue
+
+ >
+ );
+ } else {
+ return (
+
+ There are currently no test plan reports available.
+
+ );
+ }
+ };
+
+ const hasTestPlanReports = !!testPlans.length;
+
+ return (
+
+
+ Test Queue | ARIA-AT
+
+ Test Queue
+ {hasTestPlanReports && (
+
+ {isAdmin
+ ? 'Manage the test plans, assign yourself a test plan or start executing one that is already assigned to you.'
+ : isSignedIn
+ ? 'Assign yourself a test plan or start executing one that is already assigned to you.'
+ : 'Select a test plan to view. Your results will not be saved.'}
+
+ )}
+
+ {!testPlans.length ? renderNoReportsMessage() : null}
+
+ {isAdmin && (
+
+ )}
+
+ {testPlans.length
+ ? testPlans.map(testPlan => (
+
+ {/* ID needed for recovering focus after deleting a report */}
+
+ {testPlan.title}
+
+ {renderDisclosure({ testPlan })}
+
+ ))
+ : null}
+
+ );
};
export default TestQueue;
diff --git a/client/components/TestQueue/queries.js b/client/components/TestQueue/queries.js
index 6dcf3a0ce..6d83dff28 100644
--- a/client/components/TestQueue/queries.js
+++ b/client/components/TestQueue/queries.js
@@ -1,358 +1,185 @@
import { gql } from '@apollo/client';
export const TEST_QUEUE_PAGE_QUERY = gql`
- query TestQueuePage {
- me {
- id
- username
- roles
- }
- users {
- id
- username
- roles
- isBot
- ats {
- id
- key
- }
- }
- ats {
+ query TestQueuePage {
+ me {
+ id
+ username
+ roles
+ }
+ users {
+ id
+ username
+ roles
+ isBot
+ ats {
+ id
+ key
+ }
+ }
+ ats {
+ id
+ key
+ name
+ atVersions {
+ id
+ name
+ releasedAt
+ supportedByAutomation
+ }
+ browsers {
+ id
+ key
+ name
+ }
+ }
+ testPlans(testPlanVersionPhases: [DRAFT, CANDIDATE, RECOMMENDED]) {
+ directory
+ title
+ testPlanVersions {
+ id
+ title
+ phase
+ versionString
+ updatedAt
+ gitSha
+ gitMessage
+ testPlanReports(isFinal: false) {
+ id
+ at {
id
- name
key
+ name
atVersions {
- id
- name
- releasedAt
+ id
+ name
+ supportedByAutomation
+ releasedAt
}
- browsers {
- id
- key
- name
- }
- candidateBrowsers {
- id
- name
- }
- recommendedBrowsers {
- id
- name
- }
- }
- testPlanVersions {
+ }
+ browser {
id
- title
- phase
- gitSha
- gitMessage
- testPlan {
- directory
- }
- updatedAt
- }
- testPlanReports(
- isFinal: false
- testPlanVersionPhases: [DRAFT, CANDIDATE, RECOMMENDED]
- ) {
+ key
+ name
+ }
+ minimumAtVersion {
id
- conflictsLength
- runnableTestsLength
- markedFinalAt
- at {
- id
- key
- name
- }
- minimumAtVersion {
- id
- name
- }
- exactAtVersion {
- id
- name
- }
- browser {
- id
- key
- name
- }
- testPlanVersion {
- id
- title
- phase
- gitSha
- gitMessage
- testPlan {
- directory
- }
- versionString
- }
- draftTestPlanRuns {
- id
- initiatedByAutomation
- tester {
- id
- username
- isBot
- }
- testResultsLength
- }
- }
- testPlans {
- latestTestPlanVersion {
- id
- gitSha
- testPlan {
- id
- }
- }
- }
- }
-`;
-
-export const TEST_PLAN_REPORT_QUERY = gql`
- query TestPlanReport($testPlanReportId: ID!) {
- testPlanReport(id: $testPlanReportId) {
+ name
+ supportedByAutomation
+ releasedAt
+ }
+ exactAtVersion {
id
- conflictsLength
- runnableTests {
- id
- }
- runnableTestsLength
- at {
- id
- key
- name
- }
- minimumAtVersion {
- id
- name
- }
- exactAtVersion {
- id
- name
- }
- browser {
- id
- key
- name
- }
- testPlanVersion {
- id
- title
- phase
- gitSha
- gitMessage
- testPlan {
- directory
- }
- versionString
- updatedAt
- }
- draftTestPlanRuns {
- id
- initiatedByAutomation
- tester {
- id
- username
- isBot
- }
- testResults {
- id
- test {
- id
- }
- completedAt
- }
- testResultsLength
- }
- }
- }
-`;
-
-export const TEST_PLAN_REPORT_AT_BROWSER_QUERY = gql`
- query TestPlanReportAtBrowser($testPlanReportId: ID!) {
- testPlanReport(id: $testPlanReportId) {
+ name
+ supportedByAutomation
+ }
+ runnableTestsLength
+ conflictsLength
+ metrics
+ draftTestPlanRuns {
id
- at {
- id
- key
- name
+ testResultsLength
+ initiatedByAutomation
+ tester {
+ id
+ username
+ isBot
}
- browser {
- id
- key
- name
+ testResults {
+ completedAt
}
+ }
}
- }
-`;
-
-export const ADD_AT_VERSION_MUTATION = gql`
- mutation AddAtVersion($atId: ID!, $name: String!, $releasedAt: Timestamp!) {
- at(id: $atId) {
- findOrCreateAtVersion(
- input: { name: $name, releasedAt: $releasedAt }
- ) {
- id
- name
- releasedAt
+ testPlanReportStatuses {
+ testPlanReport {
+ metrics
+ draftTestPlanRuns {
+ testResults {
+ completedAt
+ }
}
+ }
}
+ }
}
-`;
-
-export const EDIT_AT_VERSION_MUTATION = gql`
- mutation EditAtVersion(
- $atVersionId: ID!
- $name: String!
- $releasedAt: Timestamp!
- ) {
- atVersion(id: $atVersionId) {
- updateAtVersion(input: { name: $name, releasedAt: $releasedAt }) {
- id
- name
- releasedAt
- }
- }
+ testPlanVersions {
+ id
+ title
+ phase
+ gitSha
+ gitMessage
+ testPlan {
+ directory
+ }
}
-`;
-
-export const DELETE_AT_VERSION_MUTATION = gql`
- mutation DeleteAtVersion($atVersionId: ID!) {
- atVersion(id: $atVersionId) {
- deleteAtVersion {
- isDeleted
- failedDueToTestResults {
- testPlanVersion {
- id
- title
- }
- # To be used when listing the conflicting results
- testResult {
- id
- }
- # To be used when providing more details on the conflicting results
- testPlanReport {
- at {
- name
- }
- browser {
- name
- }
- }
- }
- }
- }
+ testPlanReports {
+ id
}
+ }
`;
-export const ADD_TEST_QUEUE_MUTATION = gql`
- mutation AddTestPlanReport(
- $testPlanVersionId: ID!
- $atId: ID!
- $exactAtVersionId: ID
- $minimumAtVersionId: ID
- $browserId: ID!
- $copyResultsFromTestPlanVersionId: ID
- ) {
- createTestPlanReport(
- input: {
- testPlanVersionId: $testPlanVersionId
- atId: $atId
- exactAtVersionId: $exactAtVersionId
- minimumAtVersionId: $minimumAtVersionId
- browserId: $browserId
- copyResultsFromTestPlanVersionId: $copyResultsFromTestPlanVersionId
- }
- ) {
- testPlanReport {
- id
- at {
- id
- }
- browser {
- id
- }
- }
- testPlanVersion {
- id
- }
- }
+export const ASSIGN_TESTER_MUTATION = gql`
+ mutation AssignTester(
+ $testReportId: ID!
+ $testerId: ID!
+ $testPlanRunId: ID
+ ) {
+ testPlanReport(id: $testReportId) {
+ assignTester(userId: $testerId, testPlanRunId: $testPlanRunId) {
+ testPlanReport {
+ draftTestPlanRuns {
+ initiatedByAutomation
+ tester {
+ id
+ username
+ isBot
+ }
+ }
+ }
+ }
}
+ }
`;
-export const ASSIGN_TESTER_MUTATION = gql`
- mutation AssignTester(
- $testReportId: ID!
- $testerId: ID!
- $testPlanRunId: ID
- ) {
- testPlanReport(id: $testReportId) {
- assignTester(userId: $testerId, testPlanRunId: $testPlanRunId) {
- testPlanReport {
- draftTestPlanRuns {
- initiatedByAutomation
- tester {
- id
- username
- isBot
- }
- }
- }
+export const DELETE_TEST_PLAN_RUN = gql`
+ mutation DeleteTestPlanRun($testReportId: ID!, $testerId: ID!) {
+ testPlanReport(id: $testReportId) {
+ deleteTestPlanRun(userId: $testerId) {
+ testPlanReport {
+ id
+ draftTestPlanRuns {
+ id
+ tester {
+ id
+ username
+ isBot
}
+ }
}
+ }
}
+ }
`;
export const MARK_TEST_PLAN_REPORT_AS_FINAL_MUTATION = gql`
- mutation MarkTestPlanReportAsFinal(
- $testReportId: ID!
- $primaryTestPlanRunId: ID!
- ) {
- testPlanReport(id: $testReportId) {
- markAsFinal(primaryTestPlanRunId: $primaryTestPlanRunId) {
- testPlanReport {
- markedFinalAt
- }
- }
- }
+ mutation MarkTestPlanReportAsFinal(
+ $testPlanReportId: ID!
+ $primaryTestPlanRunId: ID!
+ ) {
+ testPlanReport(id: $testPlanReportId) {
+ markAsFinal(primaryTestPlanRunId: $primaryTestPlanRunId) {
+ testPlanReport {
+ markedFinalAt
+ }
+ }
}
+ }
`;
export const REMOVE_TEST_PLAN_REPORT_MUTATION = gql`
- mutation RemoveTestPlanReport($testReportId: ID!) {
- testPlanReport(id: $testReportId) {
- deleteTestPlanReport
- }
- }
-`;
-
-export const REMOVE_TESTER_MUTATION = gql`
- mutation RemoveTester($testReportId: ID!, $testerId: ID!) {
- testPlanReport(id: $testReportId) {
- deleteTestPlanRun(userId: $testerId) {
- testPlanReport {
- draftTestPlanRuns {
- tester {
- id
- username
- isBot
- }
- }
- }
- }
- }
- }
-`;
-
-export const REMOVE_TESTER_RESULTS_MUTATION = gql`
- mutation RemoveTesterResult($testPlanRunId: ID!) {
- testPlanRun(id: $testPlanRunId) {
- deleteTestResults {
- locationOfData
- }
- }
+ mutation RemoveTestPlanReport($testPlanReportId: ID!) {
+ testPlanReport(id: $testPlanReportId) {
+ deleteTestPlanReport
}
+ }
`;
diff --git a/client/components/TestQueue2/Actions.jsx b/client/components/TestQueue2/Actions.jsx
deleted file mode 100644
index c4f952b3d..000000000
--- a/client/components/TestQueue2/Actions.jsx
+++ /dev/null
@@ -1,349 +0,0 @@
-import React, { useRef } from 'react';
-import PropTypes from 'prop-types';
-import useConfirmationModal from '../../hooks/useConfirmationModal';
-import { LoadingStatus, useTriggerLoad } from '../common/LoadingStatus';
-import { useApolloClient } from '@apollo/client';
-import styled from '@emotion/styled';
-import { Button, Dropdown, Form } from 'react-bootstrap';
-import BasicModal from '../common/BasicModal';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import {
- faSquareCheck,
- faFileImport,
- faTrashAlt
-} from '@fortawesome/free-solid-svg-icons';
-import {
- MARK_TEST_PLAN_REPORT_AS_FINAL_MUTATION,
- REMOVE_TEST_PLAN_REPORT_MUTATION,
- TEST_QUEUE_PAGE_QUERY
- // TEST_PLAN_REPORT_QUERY
-} from './queries';
-import useForceUpdate from '../../hooks/useForceUpdate';
-import BasicThemedModal from '../common/BasicThemedModal';
-import { evaluateAuth } from '../../utils/evaluateAuth';
-import { TEST_PLAN_REPORT_STATUS_DIALOG_QUERY } from '../TestPlanReportStatusDialog/queries';
-import ManageBotRunDialogWithButton from '@components/ManageBotRunDialog/WithButton';
-
-const ActionContainer = styled.div`
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
-`;
-
-const Actions = ({
- me,
- testPlan,
- testPlanReport,
- testers = [],
- triggerUpdate = () => {}
-}) => {
- const primaryRunIdRef = useRef({});
-
- const { showConfirmationModal, hideConfirmationModal } =
- useConfirmationModal();
-
- const { triggerLoad, loadingMessage } = useTriggerLoad();
-
- const forceUpdate = useForceUpdate();
-
- const client = useApolloClient();
-
- const { isAdmin, isTester } = evaluateAuth(me);
-
- const selfAssignedRun =
- me &&
- testPlanReport.draftTestPlanRuns.find(
- testPlanRun => testPlanRun.tester.id === me.id
- );
-
- const nonSelfAssignedRuns = testPlanReport.draftTestPlanRuns
- .filter(testPlanRun => testPlanRun.tester.id !== me?.id)
- .sort((a, b) => a.tester.username.localeCompare(b.tester.username));
-
- const completedAllTests = testPlanReport.draftTestPlanRuns.every(
- testPlanRun =>
- testPlanRun.testResultsLength === testPlanReport.runnableTestsLength
- );
-
- const assignedBotRun = testPlanReport.draftTestPlanRuns.find(
- testPlanRun => testPlanRun.tester.isBot
- );
-
- const canMarkAsFinal =
- !assignedBotRun &&
- !testPlanReport.conflictsLength &&
- testPlanReport.draftTestPlanRuns.length > 0 &&
- testPlanReport.draftTestPlanRuns[0].testResultsLength > 0 &&
- completedAllTests;
-
- const markAsFinal = () => {
- const runs = testPlanReport.draftTestPlanRuns;
-
- primaryRunIdRef.current = runs[0].id;
-
- const onChangePrimary = event => {
- const id = event.target.value;
- primaryRunIdRef.current = id;
- forceUpdate();
- };
-
- const onConfirm = async () => {
- await triggerLoad(async () => {
- await client.mutate({
- mutation: MARK_TEST_PLAN_REPORT_AS_FINAL_MUTATION,
- refetchQueries: [
- TEST_QUEUE_PAGE_QUERY,
- TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
- ],
- awaitRefetchQueries: true,
- variables: {
- testPlanReportId: testPlanReport.id,
- primaryTestPlanRunId: primaryRunIdRef.current
- }
- });
- }, 'Marking as Final ...');
-
- hideConfirmationModal();
- };
-
- let title;
- let content;
-
- if (runs.length === 1) {
- title =
- "Are you sure you want to mark as final with a single tester's results?";
- content = (
- <>
-
- Only {runs[0].tester.username}'s results are
- included in this report, so their run will be marked as
- the primary run. Only their output will be displayed on
- report pages.
-
-
- Their run being marked as primary may also set the
- minimum required Assistive Technology Version that can
- be used for subsequent reports with this Test Plan
- Version and Assistive Technology combination.
-
- >
- );
- } else {
- // Multiple testers runs to choose from
- title = 'Select Primary Test Plan Run';
- content = (
- <>
-
- When a tester's run is marked as primary, it means
- that their output for collected results will be
- prioritized and shown on report pages.
-
-
- A tester's run being marked as primary may also set
- the minimum required Assistive Technology Version that
- can be used for subsequent reports with that Test Plan
- Version and Assistive Technology combination.
-
-
- {runs.map(run => (
-
- {run.tester.username}
-
- ))}
-
- >
- );
- }
-
- showConfirmationModal(
-
- );
- };
-
- const deleteReport = () => {
- const onConfirm = async () => {
- await triggerLoad(async () => {
- await client.mutate({
- mutation: REMOVE_TEST_PLAN_REPORT_MUTATION,
- refetchQueries: [
- TEST_QUEUE_PAGE_QUERY,
- TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
- ],
- awaitRefetchQueries: true,
- variables: { testPlanReportId: testPlanReport.id }
- });
- }, 'Deleting...');
-
- hideConfirmationModal();
-
- setTimeout(() => {
- const focusTarget =
- document.querySelector(`h2#${testPlan.directory}`) ??
- document.querySelector('#main');
-
- focusTarget.focus();
- }, 1);
- };
-
- showConfirmationModal(
- hideConfirmationModal()}
- />
- );
- };
-
- return (
-
-
- {!isTester && (
-
- View Tests
-
- )}
- {isTester && (
-
- {selfAssignedRun?.testResultsLength
- ? 'Continue Testing'
- : 'Start Testing'}
-
- )}
- {isAdmin && (
-
-
-
- Open run as...
-
-
- {nonSelfAssignedRuns.map(testPlanRun => (
-
- {testPlanRun.tester.username}
-
- ))}
-
-
- )}
- {isAdmin && assignedBotRun && (
-
- )}
- {isAdmin && (
-
-
- Mark as Final
-
- )}
- {isAdmin && (
-
-
- Delete Report
-
- )}
-
-
- );
-};
-
-Actions.propTypes = {
- me: PropTypes.shape({
- id: PropTypes.string.isRequired,
- username: PropTypes.string.isRequired
- }),
- testPlan: PropTypes.shape({
- directory: PropTypes.string.isRequired
- }).isRequired,
- testPlanReport: PropTypes.shape({
- id: PropTypes.string.isRequired,
- runnableTestsLength: PropTypes.number.isRequired,
- conflictsLength: PropTypes.number.isRequired,
- draftTestPlanRuns: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- testResultsLength: PropTypes.number.isRequired,
- tester: PropTypes.shape({
- id: PropTypes.string.isRequired,
- username: PropTypes.string.isRequired,
- isBot: PropTypes.bool.isRequired
- })
- })
- ).isRequired
- }).isRequired,
- testers: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- username: PropTypes.string.isRequired,
- isBot: PropTypes.bool.isRequired,
- ats: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired
- })
- )
- })
- ).isRequired,
- triggerUpdate: PropTypes.func.isRequired
-};
-
-export default Actions;
diff --git a/client/components/TestQueue2/AssignTesters.jsx b/client/components/TestQueue2/AssignTesters.jsx
deleted file mode 100644
index 60afd6098..000000000
--- a/client/components/TestQueue2/AssignTesters.jsx
+++ /dev/null
@@ -1,382 +0,0 @@
-import React, { useRef } from 'react';
-import PropTypes from 'prop-types';
-import styled from '@emotion/styled';
-import { useApolloClient } from '@apollo/client';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faCheck, faRobot, faUser } from '@fortawesome/free-solid-svg-icons';
-import { Button, Dropdown } from 'react-bootstrap';
-import CompletionStatusListItem from './CompletionStatusListItem';
-import useConfirmationModal from '../../hooks/useConfirmationModal';
-import BasicThemedModal from '../common/BasicThemedModal';
-import { LoadingStatus, useTriggerLoad } from '../common/LoadingStatus';
-import { useAriaLiveRegion } from '../providers/AriaLiveRegionProvider';
-import { evaluateAuth } from '../../utils/evaluateAuth';
-import { isSupportedByResponseCollector } from '../../utils/automation';
-import {
- ASSIGN_TESTER_MUTATION,
- DELETE_TEST_PLAN_RUN,
- TEST_QUEUE_PAGE_QUERY
-} from './queries';
-import { SCHEDULE_COLLECTION_JOB_MUTATION } from '../AddTestToQueueWithConfirmation/queries';
-import { TEST_PLAN_REPORT_STATUS_DIALOG_QUERY } from '../TestPlanReportStatusDialog/queries';
-
-const AssignTestersContainer = styled.div`
- display: flex;
-
- [role='menu'] {
- width: 250px;
- max-height: 200px;
- overflow-y: scroll;
- }
-
- [role='menuitem']:active {
- background-color: #0b60ab;
- }
-`;
-
-const AssignTestersDropdownButton = styled(Dropdown.Toggle)`
- width: min-content !important;
- margin-right: 0.5rem;
-`;
-
-const AssignedTestersUl = styled.ul`
- font-weight: normal;
- padding-top: 0.5rem;
- text-align: center;
-
- li:not(:last-of-type) {
- padding-bottom: 0.25rem;
- }
- a,
- span {
- font-weight: normal;
- padding-right: 0.5rem;
- }
- em {
- color: rgb(var(--bs-secondary-rgb));
- font-style: normal;
- display: inline-block;
- }
-`;
-
-const AssignTesters = ({ me, testers, testPlanReport }) => {
- const { triggerLoad, loadingMessage } = useTriggerLoad();
-
- const setAlertMessage = useAriaLiveRegion();
-
- const { showConfirmationModal, hideConfirmationModal } =
- useConfirmationModal();
-
- const client = useApolloClient();
-
- const dropdownButtonRef = useRef();
- const assignSelfButtonRef = useRef();
-
- const { isAdmin, isTester } = evaluateAuth(me);
-
- const isSelfAssigned =
- me &&
- testPlanReport.draftTestPlanRuns.some(
- testPlanRun => testPlanRun.tester.id === me.id
- );
-
- const onToggle = isShown => {
- setTimeout(() => {
- if (!isShown) return;
- document
- .querySelector(
- `#assign-testers-${testPlanReport.id} [role="menuitemcheckbox"]`
- )
- .focus();
- }, 1);
- };
-
- const onKeyDown = event => {
- const { key } = event;
- if (key.match(/[0-9a-zA-Z]/)) {
- const container = event.target.closest('[role=menu]');
- const matchingMenuItem = Array.from(container.children).find(
- menuItem => {
- return menuItem.innerText
- .trim()
- .toLowerCase()
- .startsWith(key.toLowerCase());
- }
- );
-
- if (matchingMenuItem) {
- matchingMenuItem.focus();
- }
- }
- };
-
- const toggleTesterCommon = async ({
- testerId,
- assignedCallback,
- unassignedCallback
- }) => {
- const tester = testers.find(tester => tester.id === testerId);
-
- const isAssigned = testPlanReport.draftTestPlanRuns.some(
- testPlanRun => testPlanRun.tester.id === testerId
- );
-
- if (isAssigned) {
- const isSelfAssigned = tester.id === me.id;
- const testerFormatted = isSelfAssigned
- ? 'your'
- : `${tester.username}'s`;
-
- const onConfirm = async () => {
- await triggerLoad(async () => {
- await client.mutate({
- mutation: DELETE_TEST_PLAN_RUN,
- refetchQueries: [
- TEST_QUEUE_PAGE_QUERY,
- TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
- ],
- awaitRefetchQueries: true,
- variables: {
- testReportId: testPlanReport.id,
- testerId: tester.id
- }
- });
- }, 'Deleting...');
-
- hideConfirmationModal();
-
- await assignedCallback?.({ tester });
- };
-
- showConfirmationModal(
- hideConfirmationModal()}
- />
- );
-
- return;
- }
-
- await triggerLoad(async () => {
- if (tester.isBot) {
- await client.mutate({
- mutation: SCHEDULE_COLLECTION_JOB_MUTATION,
- refetchQueries: [
- TEST_QUEUE_PAGE_QUERY,
- TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
- ],
- awaitRefetchQueries: true,
- variables: {
- testPlanReportId: testPlanReport.id
- }
- });
- } else {
- await client.mutate({
- mutation: ASSIGN_TESTER_MUTATION,
- refetchQueries: [
- TEST_QUEUE_PAGE_QUERY,
- TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
- ],
- awaitRefetchQueries: true,
- variables: {
- testReportId: testPlanReport.id,
- testerId: tester.id
- }
- });
- }
- }, 'Assigning...');
-
- await unassignedCallback?.({ tester });
- };
-
- const onSelect = testerId => {
- const assignedCallback = ({ tester }) => {
- setAlertMessage(`${tester.username}'s run has been deleted`);
-
- setTimeout(() => {
- dropdownButtonRef.current.focus();
- }, 1);
- };
-
- const unassignedCallback = ({ tester }) => {
- setAlertMessage(`Assigned ${tester.username}`);
-
- setTimeout(() => {
- dropdownButtonRef.current.focus();
- }, 1);
- };
-
- toggleTesterCommon({ testerId, assignedCallback, unassignedCallback });
- };
-
- const onToggleSelf = () => {
- const assignedCallback = () => {
- setTimeout(() => {
- assignSelfButtonRef.current.focus();
- }, 1);
- };
-
- toggleTesterCommon({
- testerId: me.id,
- assignedCallback
- });
- };
-
- const renderDropdownItem = ({ tester }) => {
- const { id, username, isBot, ats } = tester;
-
- if (isBot) {
- const foundAtForBot = ats.find(
- ({ id }) => id === testPlanReport.at?.id
- );
- const supportedByResponseCollector = isSupportedByResponseCollector(
- {
- id: testPlanReport.id,
- at: testPlanReport.at,
- browser: testPlanReport.browser
- }
- );
- if (!foundAtForBot || !supportedByResponseCollector) return null;
- }
-
- const isAssigned = testPlanReport.draftTestPlanRuns.some(
- testPlanRun => testPlanRun.tester.username === username
- );
-
- let icon;
- if (isAssigned) icon = faCheck;
- else if (isBot) icon = faRobot;
-
- return (
-
-
- {icon && }
- {`${username}`}
-
-
- );
- };
-
- return (
-
-
- {isAdmin && (
-
-
- Assign Testers
-
-
-
- {testers.map(tester =>
- renderDropdownItem({ tester })
- )}
-
-
- )}
- {isTester && (
-
- {isSelfAssigned
- ? 'Unassign Yourself'
- : 'Assign Yourself'}
-
- )}
-
-
- {testPlanReport.draftTestPlanRuns
- .slice()
- .sort((a, b) =>
- a.tester.username.localeCompare(b.tester.username)
- )
- .map((testPlanRun, index) => {
- const tester = testPlanRun.tester;
- const rowId = `plan-${testPlanReport.id}-assignee-${tester.id}-run-${testPlanRun.id}`;
- return (
-
-
- {index ===
- testPlanReport.draftTestPlanRuns.length -
- 1 ? null : (
-
- )}
-
- );
- })}
-
-
- );
-};
-
-AssignTesters.propTypes = {
- me: PropTypes.shape({
- id: PropTypes.string.isRequired
- }),
- testers: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- username: PropTypes.string.isRequired,
- isBot: PropTypes.bool.isRequired,
- ats: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired
- })
- )
- })
- ).isRequired,
- testPlanReport: PropTypes.shape({
- id: PropTypes.string.isRequired,
- runnableTestsLength: PropTypes.number.isRequired,
- draftTestPlanRuns: PropTypes.arrayOf(
- PropTypes.shape({
- tester: PropTypes.shape({ id: PropTypes.string.isRequired })
- .isRequired
- })
- ).isRequired,
- at: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired
- }).isRequired,
- browser: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- key: PropTypes.string.isRequired
- }).isRequired
- }).isRequired
-};
-
-export default AssignTesters;
diff --git a/client/components/TestQueue2/CompletionStatusListItem/index.jsx b/client/components/TestQueue2/CompletionStatusListItem/index.jsx
deleted file mode 100644
index 9e55a58a1..000000000
--- a/client/components/TestQueue2/CompletionStatusListItem/index.jsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import React, { useMemo } from 'react';
-import PropTypes from 'prop-types';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faRobot } from '@fortawesome/free-solid-svg-icons';
-import BotTestCompletionStatus from '@components/TestQueueCompletionStatusListItem/BotTestCompletionStatus';
-import PreviouslyAutomatedTestCompletionStatus from '@components/TestQueueCompletionStatusListItem/PreviouslyAutomatedTestCompletionStatus';
-
-const CompletionStatusListItem = ({
- rowId,
- testPlanReport,
- testPlanRun,
- tester
-}) => {
- const { username, isBot } = tester;
- const testPlanRunPreviouslyAutomated = useMemo(
- () => testPlanRun.initiatedByAutomation,
- [testPlanRun]
- );
-
- let info;
- let completionStatus;
-
- if (isBot) {
- info = (
-
-
- {username}
-
- );
- completionStatus = (
-
- );
- } else {
- info = (
-
- {username}
-
- );
-
- completionStatus = testPlanRunPreviouslyAutomated ? (
-
- ) : (
-
- {`${testPlanRun.testResultsLength} of ` +
- `${testPlanReport.runnableTestsLength} tests complete`}
-
- );
- }
-
- return (
-
- {info}
- {completionStatus}
-
- );
-};
-
-// TODO: Update shape for testPlanReport and tester
-CompletionStatusListItem.propTypes = {
- rowId: PropTypes.string.isRequired,
- testPlanReport: PropTypes.object.isRequired,
- testPlanRun: PropTypes.shape({
- id: PropTypes.string.isRequired,
- testResultsLength: PropTypes.number.isRequired,
- initiatedByAutomation: PropTypes.bool.isRequired,
- tester: PropTypes.shape({
- username: PropTypes.string.isRequired,
- isBot: PropTypes.bool.isRequired
- }).isRequired
- }).isRequired,
- tester: PropTypes.object.isRequired
-};
-
-export default CompletionStatusListItem;
diff --git a/client/components/TestQueue2/TestQueue2.test.js b/client/components/TestQueue2/TestQueue2.test.js
deleted file mode 100644
index 3dee29794..000000000
--- a/client/components/TestQueue2/TestQueue2.test.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import getPage from '../../tests/util/getPage';
-
-describe('Test Queue', () => {
- const clearEntireTestQueue = () => {
- console.error('TODO: IMPLEMENT clearEntireTestQueue');
- };
-
- it('renders Test Queue page h1', async () => {
- await getPage({ role: 'admin', url: '/test-queue' }, async page => {
- const h1Element = await page.$eval(
- 'h1',
- element => element.textContent
- );
- expect(h1Element).toBe('Test Queue');
- });
- });
-
- it.skip('renders error message when no test plan reports exist', async () => {
- await getPage({ role: 'admin', url: '/test-queue' }, async page => {
- await clearEntireTestQueue();
- await page.waitForSelector(
- '::-p-text(There are currently no test plan reports to show.)'
- );
- });
- expect(true).toBe(false);
- });
-});
diff --git a/client/components/TestQueue2/index.jsx b/client/components/TestQueue2/index.jsx
deleted file mode 100644
index 72e3b162f..000000000
--- a/client/components/TestQueue2/index.jsx
+++ /dev/null
@@ -1,416 +0,0 @@
-import React, { Fragment, useRef } from 'react';
-import { useApolloClient, useQuery } from '@apollo/client';
-import PageStatus from '../common/PageStatus';
-import { TEST_QUEUE_PAGE_QUERY } from './queries';
-import { Container, Table as BootstrapTable } from 'react-bootstrap';
-import { Helmet } from 'react-helmet';
-import { evaluateAuth } from '../../utils/evaluateAuth';
-import ManageTestQueue from '../ManageTestQueue';
-import DisclosureComponentUnstyled from '../common/DisclosureComponent';
-import useForceUpdate from '../../hooks/useForceUpdate';
-import styled from '@emotion/styled';
-import VersionString from '../common/VersionString';
-import PhasePill from '../common/PhasePill';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faArrowUpRightFromSquare } from '@fortawesome/free-solid-svg-icons';
-import TestPlanReportStatusDialogWithButton from '../TestPlanReportStatusDialog/WithButton';
-import ReportStatusSummary from '../common/ReportStatusSummary';
-import { AtVersion, BrowserVersion } from '../common/AtBrowserVersion';
-import { calculatePercentComplete } from '../../utils/calculatePercentComplete';
-import ProgressBar from '../common/ClippedProgressBar';
-import AssignTesters from './AssignTesters';
-import Actions from './Actions';
-import BotRunTestStatusList from '../BotRunTestStatusList';
-
-const DisclosureComponent = styled(DisclosureComponentUnstyled)`
- h3 {
- font-size: 1rem;
-
- button {
- font-size: unset;
- font-weight: unset;
- }
- }
-
- [role='region'] {
- padding: 0;
- }
-`;
-
-const MetadataContainer = styled.div`
- display: flex;
- gap: 1.25em;
- margin: 0.5rem 1.25rem;
- align-items: center;
- min-height: 40px; /* temp because the status dialog button keeps disappearing */
-
- & button {
- margin-bottom: 0;
- margin-top: 0;
- font-size: 16px;
- }
- & button:hover {
- color: white;
- }
- & button,
- & button:focus {
- color: #2e2f33;
- }
-`;
-
-const TableOverflowContainer = styled.div`
- width: 100%;
-
- @media (max-width: 1080px) {
- overflow-x: scroll;
- }
-`;
-
-const Table = styled(BootstrapTable)`
- margin-bottom: 0;
-
- th {
- padding: 0.75rem;
- }
-
- th:first-of-type,
- td:first-of-type {
- border-left: none;
- }
- th:last-of-type,
- td:last-of-type {
- border-right: none;
- }
- tr:last-of-type,
- tr:last-of-type td {
- border-bottom: none;
- }
-
- th:nth-of-type(1),
- td:nth-of-type(1) {
- min-width: 220px;
- }
- th:nth-of-type(2),
- td:nth-of-type(2) {
- min-width: 150px;
- }
- th:nth-of-type(3),
- td:nth-of-type(3) {
- min-width: 230px;
- }
- th:nth-of-type(4),
- td:nth-of-type(4) {
- width: 20%;
- min-width: 125px;
- }
- th:nth-of-type(5),
- td:nth-of-type(5) {
- width: 20%;
- min-width: 175px;
- }
-`;
-
-const StatusContainer = styled.div`
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- text-align: center;
- color: rgb(var(--bs-secondary-rgb));
-`;
-
-const TestQueue = () => {
- const client = useApolloClient();
- const { data, error, refetch } = useQuery(TEST_QUEUE_PAGE_QUERY, {
- fetchPolicy: 'cache-and-network'
- });
-
- const openDisclosuresRef = useRef({});
- const forceUpdate = useForceUpdate();
-
- if (error) {
- return (
-
- );
- }
-
- if (!data) {
- return (
-
- );
- }
-
- const isSignedIn = !!data.me;
-
- const { isAdmin } = evaluateAuth(data.me);
-
- const testPlanVersions = [];
- data.testPlans.forEach(testPlan => {
- // testPlan.directory is needed by ManageTestQueue
- const populatedTestPlanVersions = testPlan.testPlanVersions.map(
- testPlanVersion => ({
- ...testPlanVersion,
- testPlan: { directory: testPlan.directory }
- })
- );
- testPlanVersions.push(...populatedTestPlanVersions);
- });
-
- // Remove any test plans or test plan versions without reports and sort
- const sortTestPlanVersions = testPlanVersions => {
- return [...testPlanVersions]
- .filter(testPlanVersion => testPlanVersion.testPlanReports.length)
- .sort((a, b) => {
- return b.versionString.localeCompare(a.versionString);
- })
- .map(testPlanVersion => {
- return {
- ...testPlanVersion,
- testPlanReports: sortTestPlanReports(
- testPlanVersion.testPlanReports
- )
- };
- });
- };
-
- const sortTestPlanReports = testPlanReports => {
- return [...testPlanReports].sort((a, b) => {
- if (a.at.name !== b.at.name) {
- return a.at.name.localeCompare(b.at.name);
- }
- if (a.browser.name !== b.browser.name) {
- return a.browser.name.localeCompare(b.browser.name);
- }
- const dateA = new Date(
- (a.minimumAtVersion ?? a.exactAtVersion).releasedAt
- );
- const dateB = new Date(
- (a.minimumAtVersion ?? a.exactAtVersion).releasedAt
- );
- return dateB - dateA;
- });
- };
-
- const testPlans = data.testPlans
- .filter(testPlan => {
- for (const testPlanVersion of testPlan.testPlanVersions) {
- if (testPlanVersion.testPlanReports.length) return true;
- }
- })
- .map(testPlan => {
- return {
- ...testPlan,
- testPlanVersions: sortTestPlanVersions(
- testPlan.testPlanVersions
- )
- };
- })
- .sort((a, b) => {
- return a.title.localeCompare(b.title);
- });
-
- const testers = data.users
- .filter(user => user.roles.includes('TESTER'))
- .sort((a, b) => a.username.localeCompare(b.username));
-
- const renderDisclosure = ({ testPlan }) => {
- return (
- // TODO: fix the aria-label of this
- (
- <>
-
- {testPlanVersion.versionString}
-
-
-
- {testPlanVersion.phase}
-
- >
- ))}
- onClick={testPlan.testPlanVersions.map(
- testPlanVersion => () => {
- const isOpen =
- openDisclosuresRef.current[testPlanVersion.id];
- openDisclosuresRef.current[testPlanVersion.id] =
- !isOpen;
- forceUpdate();
- }
- )}
- expanded={testPlan.testPlanVersions.map(
- testPlanVersion =>
- openDisclosuresRef.current[testPlanVersion.id] || false
- )}
- disclosureContainerView={testPlan.testPlanVersions.map(
- testPlanVersion =>
- renderDisclosureContent({ testPlan, testPlanVersion })
- )}
- />
- );
- };
-
- const renderDisclosureContent = ({ testPlan, testPlanVersion }) => {
- return (
- <>
-
-
-
- View tests in {testPlanVersion.versionString}
-
-
-
-
-
-
-
- Assistive Technology
- Browser
- Testers
- Status
- Actions
-
-
-
- {testPlanVersion.testPlanReports.map(
- testPlanReport =>
- renderRow({
- testPlan,
- testPlanVersion,
- testPlanReport
- })
- )}
-
-
-
- >
- );
- };
-
- const renderRow = ({ testPlan, testPlanVersion, testPlanReport }) => {
- const percentComplete = calculatePercentComplete(testPlanReport);
- const hasBotRun = testPlanReport.draftTestPlanRuns?.some(
- ({ tester }) => tester.isBot
- );
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- { }
-
- {hasBotRun ? (
-
- ) : null}
-
-
-
- {
- await client.refetchQueries({
- include: [
- 'TestQueuePage',
- 'TestPlanReportStatusDialog'
- ]
- });
-
- // Refocus on testers assignment dropdown button
- const selector = `#assign-testers-${testPlanReport.id} button`;
- document.querySelector(selector).focus();
- }}
- />
-
-
- );
- };
-
- return (
-
-
- Test Queue | ARIA-AT
-
- Test Queue
-
- {isSignedIn
- ? 'Assign yourself a test plan or start executing one that is already assigned to you.'
- : 'Select a test plan to view. Your results will not be saved.'}
-
- {isAdmin && (
-
- )}
-
- {!testPlans.length
- ? 'There are currently no test plan reports to show.'
- : testPlans.map(testPlan => (
-
- {/* ID needed for recovering focus after deleting a report */}
-
- {testPlan.title}
-
- {renderDisclosure({ testPlan })}
-
- ))}
-
- );
-};
-
-export default TestQueue;
diff --git a/client/components/TestQueue2/queries.js b/client/components/TestQueue2/queries.js
deleted file mode 100644
index 7b5513d36..000000000
--- a/client/components/TestQueue2/queries.js
+++ /dev/null
@@ -1,175 +0,0 @@
-import { gql } from '@apollo/client';
-
-export const TEST_QUEUE_PAGE_QUERY = gql`
- query TestQueuePage {
- me {
- id
- username
- roles
- }
- users {
- id
- username
- roles
- isBot
- ats {
- id
- key
- }
- }
- ats {
- id
- key
- name
- atVersions {
- id
- name
- releasedAt
- }
- browsers {
- id
- key
- name
- }
- }
- testPlans(testPlanVersionPhases: [DRAFT, CANDIDATE, RECOMMENDED]) {
- directory
- title
- testPlanVersions {
- id
- title
- phase
- versionString
- updatedAt
- gitSha
- gitMessage
- testPlanReports(isFinal: false) {
- id
- at {
- id
- key
- name
- }
- browser {
- id
- key
- name
- }
- minimumAtVersion {
- id
- name
- }
- exactAtVersion {
- id
- name
- }
- runnableTestsLength
- conflictsLength
- metrics
- draftTestPlanRuns {
- id
- testResultsLength
- initiatedByAutomation
- tester {
- id
- username
- isBot
- }
- testResults {
- completedAt
- }
- }
- }
- testPlanReportStatuses {
- testPlanReport {
- metrics
- draftTestPlanRuns {
- testResults {
- completedAt
- }
- }
- }
- }
- }
- }
- testPlanVersions {
- id
- title
- phase
- gitSha
- gitMessage
- testPlan {
- directory
- }
- }
- testPlanReports {
- id
- }
- }
-`;
-
-export const ASSIGN_TESTER_MUTATION = gql`
- mutation AssignTester(
- $testReportId: ID!
- $testerId: ID!
- $testPlanRunId: ID
- ) {
- testPlanReport(id: $testReportId) {
- assignTester(userId: $testerId, testPlanRunId: $testPlanRunId) {
- testPlanReport {
- draftTestPlanRuns {
- initiatedByAutomation
- tester {
- id
- username
- isBot
- }
- }
- }
- }
- }
- }
-`;
-
-export const DELETE_TEST_PLAN_RUN = gql`
- mutation DeleteTestPlanRun($testReportId: ID!, $testerId: ID!) {
- testPlanReport(id: $testReportId) {
- deleteTestPlanRun(userId: $testerId) {
- testPlanReport {
- id
- draftTestPlanRuns {
- id
- tester {
- id
- username
- isBot
- }
- }
- }
- }
- }
- }
-`;
-
-export const MARK_TEST_PLAN_REPORT_AS_FINAL_MUTATION = gql`
- mutation MarkTestPlanReportAsFinal(
- $testPlanReportId: ID!
- $primaryTestPlanRunId: ID!
- ) {
- testPlanReport(id: $testPlanReportId) {
- markAsFinal(primaryTestPlanRunId: $primaryTestPlanRunId) {
- testPlanReport {
- markedFinalAt
- }
- }
- }
- }
-`;
-
-export const REMOVE_TEST_PLAN_REPORT_MUTATION = gql`
- mutation RemoveTestPlanReport($testPlanReportId: ID!) {
- testPlanReport(id: $testPlanReportId) {
- deleteTestPlanReport
- }
- }
-`;
diff --git a/client/components/TestQueueCompletionStatusListItem/BotTestCompletionStatus/index.js b/client/components/TestQueueCompletionStatusListItem/BotTestCompletionStatus/index.js
index a395cc48e..0368b1165 100644
--- a/client/components/TestQueueCompletionStatusListItem/BotTestCompletionStatus/index.js
+++ b/client/components/TestQueueCompletionStatusListItem/BotTestCompletionStatus/index.js
@@ -2,70 +2,53 @@ import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useTestPlanRunValidatedAssertionCounts } from '../../../hooks/useTestPlanRunValidatedAssertionCounts';
-const BotTestCompletionStatus = ({
- testPlanRun,
- id,
- runnableTestsLength,
- fromTestQueueV2 = false // TODO: Remove when Test Queue v1 is removed
-}) => {
- const {
- totalValidatedAssertions,
- totalPossibleAssertions,
- testResultsLength,
- stopPolling
- } = useTestPlanRunValidatedAssertionCounts(testPlanRun, 2000);
+const BotTestCompletionStatus = ({ testPlanRun, id, runnableTestsLength }) => {
+ const {
+ totalValidatedAssertions,
+ totalPossibleAssertions,
+ testResultsLength,
+ stopPolling
+ } = useTestPlanRunValidatedAssertionCounts(testPlanRun, 2000);
- useEffect(() => {
- if (testResultsLength === runnableTestsLength) {
- stopPolling();
- }
- }, [testResultsLength, stopPolling]);
+ useEffect(() => {
+ if (testResultsLength === runnableTestsLength) {
+ stopPolling();
+ }
+ }, [testResultsLength, stopPolling]);
- return (
- <>
- {fromTestQueueV2 ? (
-
-
- {`Responses for ${testResultsLength} of ${runnableTestsLength} tests recorded`}
-
-
- {`Verdicts for ${totalValidatedAssertions} of ${totalPossibleAssertions} assertions assigned`}
-
-
- ) : (
-
-
- {`Responses for ${testResultsLength} of ${runnableTestsLength} tests recorded`}
-
-
- {`Verdicts for ${totalValidatedAssertions} of ${totalPossibleAssertions} assertions assigned`}
-
-
- )}
- >
- );
+ return (
+ <>
+
+
+ {`Responses for ${testResultsLength} of ${runnableTestsLength} tests recorded`}
+
+
+ {`Verdicts for ${totalValidatedAssertions} of ${totalPossibleAssertions} assertions assigned`}
+
+
+ >
+ );
};
BotTestCompletionStatus.propTypes = {
- testPlanRun: PropTypes.shape({
- id: PropTypes.string.isRequired,
- testResults: PropTypes.arrayOf(
- PropTypes.shape({
- scenarioResults: PropTypes.arrayOf(
- PropTypes.shape({
- assertionResults: PropTypes.arrayOf(
- PropTypes.shape({
- passed: PropTypes.bool
- })
- )
- })
- )
- })
- )
- }).isRequired,
+ testPlanRun: PropTypes.shape({
id: PropTypes.string.isRequired,
- runnableTestsLength: PropTypes.number.isRequired,
- fromTestQueueV2: PropTypes.bool
+ testResults: PropTypes.arrayOf(
+ PropTypes.shape({
+ scenarioResults: PropTypes.arrayOf(
+ PropTypes.shape({
+ assertionResults: PropTypes.arrayOf(
+ PropTypes.shape({
+ passed: PropTypes.bool
+ })
+ )
+ })
+ )
+ })
+ )
+ }).isRequired,
+ id: PropTypes.string.isRequired,
+ runnableTestsLength: PropTypes.number.isRequired
};
export default BotTestCompletionStatus;
diff --git a/client/components/TestQueueCompletionStatusListItem/PreviouslyAutomatedTestCompletionStatus/index.js b/client/components/TestQueueCompletionStatusListItem/PreviouslyAutomatedTestCompletionStatus/index.js
index 1ac71b589..1b5fc8fe2 100644
--- a/client/components/TestQueueCompletionStatusListItem/PreviouslyAutomatedTestCompletionStatus/index.js
+++ b/client/components/TestQueueCompletionStatusListItem/PreviouslyAutomatedTestCompletionStatus/index.js
@@ -4,60 +4,52 @@ import { useQuery } from '@apollo/client';
import { TEST_PLAN_RUN_ASSERTION_RESULTS_QUERY } from '../queries';
const PreviouslyAutomatedTestCompletionStatus = ({
- runnableTestsLength,
- testPlanRunId,
- id,
- fromTestQueueV2 = false // TODO: Remove when Test Queue v1 is removed
+ runnableTestsLength,
+ testPlanRunId,
+ id
}) => {
- const { data: testPlanRunAssertionsQueryResult } = useQuery(
- TEST_PLAN_RUN_ASSERTION_RESULTS_QUERY,
- {
- variables: {
- testPlanRunId: testPlanRunId
- },
- fetchPolicy: 'cache-and-network'
- }
- );
-
- const numValidatedTests = useMemo(() => {
- if (!testPlanRunAssertionsQueryResult) {
- return 0;
- }
-
- const { testPlanRun } = testPlanRunAssertionsQueryResult;
-
- if (!testPlanRun) {
- return 0;
- }
-
- const { testResults = [] } = testPlanRun;
-
- return testResults.reduce(
- (acc, { completedAt }) => acc + (completedAt ? 1 : 0),
- 0
- );
- }, [testPlanRunAssertionsQueryResult]);
-
- return (
- <>
- {fromTestQueueV2 ? (
-
- {`${numValidatedTests} of ${runnableTestsLength} tests evaluated`}
-
- ) : (
-
- {`${numValidatedTests} of ${runnableTestsLength} tests evaluated`}
-
- )}
- >
+ const { data: testPlanRunAssertionsQueryResult } = useQuery(
+ TEST_PLAN_RUN_ASSERTION_RESULTS_QUERY,
+ {
+ variables: {
+ testPlanRunId: testPlanRunId
+ },
+ fetchPolicy: 'cache-and-network'
+ }
+ );
+
+ const numValidatedTests = useMemo(() => {
+ if (!testPlanRunAssertionsQueryResult) {
+ return 0;
+ }
+
+ const { testPlanRun } = testPlanRunAssertionsQueryResult;
+
+ if (!testPlanRun) {
+ return 0;
+ }
+
+ const { testResults = [] } = testPlanRun;
+
+ return testResults.reduce(
+ (acc, { completedAt }) => acc + (completedAt ? 1 : 0),
+ 0
);
+ }, [testPlanRunAssertionsQueryResult]);
+
+ return (
+ <>
+
+ {`${numValidatedTests} of ${runnableTestsLength} tests evaluated`}
+
+ >
+ );
};
PreviouslyAutomatedTestCompletionStatus.propTypes = {
- runnableTestsLength: PropTypes.number.isRequired,
- testPlanRunId: PropTypes.string.isRequired,
- id: PropTypes.string.isRequired,
- fromTestQueueV2: PropTypes.bool
+ runnableTestsLength: PropTypes.number.isRequired,
+ testPlanRunId: PropTypes.string.isRequired,
+ id: PropTypes.string.isRequired
};
export default PreviouslyAutomatedTestCompletionStatus;
diff --git a/client/components/TestQueueCompletionStatusListItem/index.js b/client/components/TestQueueCompletionStatusListItem/index.js
index 1ade96353..165ad5a47 100644
--- a/client/components/TestQueueCompletionStatusListItem/index.js
+++ b/client/components/TestQueueCompletionStatusListItem/index.js
@@ -6,87 +6,87 @@ import BotTestCompletionStatus from './BotTestCompletionStatus';
import PreviouslyAutomatedTestCompletionStatus from './PreviouslyAutomatedTestCompletionStatus';
const TestQueueCompletionStatusListItem = ({
- runnableTestsLength,
- testPlanRun,
- id
+ runnableTestsLength,
+ testPlanRun,
+ id
}) => {
- const { testResultsLength, tester } = testPlanRun;
- const testPlanRunPreviouslyAutomated = useMemo(
- () => testPlanRun.initiatedByAutomation,
- [testPlanRun]
- );
+ const { testResultsLength, tester } = testPlanRun;
+ const testPlanRunPreviouslyAutomated = useMemo(
+ () => testPlanRun.initiatedByAutomation,
+ [testPlanRun]
+ );
- const renderTesterInfo = () => {
- if (tester.isBot) {
- return (
-
-
- {tester.username}
-
- );
- } else {
- return (
-
- {tester.username}
-
- );
- }
- };
+ const renderTesterInfo = () => {
+ if (tester.isBot) {
+ return (
+
+
+ {tester.username}
+
+ );
+ } else {
+ return (
+
+ {tester.username}
+
+ );
+ }
+ };
- const renderTestCompletionStatus = () => {
- if (tester.isBot) {
- return (
-
- );
- } else if (testPlanRunPreviouslyAutomated) {
- return (
-
- );
- } else {
- return (
-
- {`${testResultsLength} of ${runnableTestsLength} tests complete`}
-
- );
- }
- };
+ const renderTestCompletionStatus = () => {
+ if (tester.isBot) {
+ return (
+
+ );
+ } else if (testPlanRunPreviouslyAutomated) {
+ return (
+
+ );
+ } else {
+ return (
+
+ {`${testResultsLength} of ${runnableTestsLength} tests complete`}
+
+ );
+ }
+ };
- return (
-
- {renderTesterInfo()}
- {renderTestCompletionStatus()}
-
- );
+ return (
+
+ {renderTesterInfo()}
+ {renderTestCompletionStatus()}
+
+ );
};
TestQueueCompletionStatusListItem.propTypes = {
- runnableTestsLength: PropTypes.number.isRequired,
- testPlanRun: PropTypes.shape({
- id: PropTypes.string.isRequired,
- testResultsLength: PropTypes.number.isRequired,
- initiatedByAutomation: PropTypes.bool.isRequired,
- tester: PropTypes.shape({
- username: PropTypes.string.isRequired,
- isBot: PropTypes.bool.isRequired
- }).isRequired
- }).isRequired,
- id: PropTypes.string.isRequired
+ runnableTestsLength: PropTypes.number.isRequired,
+ testPlanRun: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ testResultsLength: PropTypes.number.isRequired,
+ initiatedByAutomation: PropTypes.bool.isRequired,
+ tester: PropTypes.shape({
+ username: PropTypes.string.isRequired,
+ isBot: PropTypes.bool.isRequired
+ }).isRequired
+ }).isRequired,
+ id: PropTypes.string.isRequired
};
export default TestQueueCompletionStatusListItem;
diff --git a/client/components/TestQueueCompletionStatusListItem/queries.js b/client/components/TestQueueCompletionStatusListItem/queries.js
index 561579bd4..9340524e1 100644
--- a/client/components/TestQueueCompletionStatusListItem/queries.js
+++ b/client/components/TestQueueCompletionStatusListItem/queries.js
@@ -1,19 +1,19 @@
import { gql } from '@apollo/client';
export const TEST_PLAN_RUN_ASSERTION_RESULTS_QUERY = gql`
- query TestPlanRunAssertionResultsByTestPlanRunId($testPlanRunId: ID!) {
- testPlanRun(id: $testPlanRunId) {
- id
- testResults {
- id
- completedAt
- scenarioResults {
- assertionResults {
- passed
- failedReason
- }
- }
- }
+ query TestPlanRunAssertionResultsByTestPlanRunId($testPlanRunId: ID!) {
+ testPlanRun(id: $testPlanRunId) {
+ id
+ testResults {
+ id
+ completedAt
+ scenarioResults {
+ assertionResults {
+ passed
+ failedReason
+ }
}
+ }
}
+ }
`;
diff --git a/client/components/TestQueueRow/TestQueueRow.css b/client/components/TestQueueRow/TestQueueRow.css
deleted file mode 100644
index b24d56deb..000000000
--- a/client/components/TestQueueRow/TestQueueRow.css
+++ /dev/null
@@ -1,88 +0,0 @@
-.testers-wrapper {
- display: flex;
- flex-direction: row;
- min-height: 3.75rem;
-}
-
-.testers-wrapper div {
- flex-basis: auto;
-}
-
-.testers-wrapper .dropdown {
- padding-right: 0.75em;
-}
-
-.testers-wrapper .assign-actions {
- width: 100%;
-}
-
-.fullname {
- color: gray;
- font-size: 0.9em;
- margin-left: 0.25em;
-}
-
-svg.svg-inline--fa {
- margin-right: 0.5em;
-}
-
-button.more-actions svg {
- color: #9fa4aa;
-}
-
-button.more-actions {
- background: transparent;
- color: inherit;
- border: none;
- padding: 0;
- cursor: pointer;
- outline: inherit;
-}
-
-button.more-actions:hover svg,
-button.more-actions:focus svg {
- color: #6d757d;
-}
-
-button.more-actions.btn-primary:hover,
-button.more-actions.btn-primary:focus {
- background: transparent;
-}
-
-button.more-actions:active {
- background: #0053ba;
-}
-
-.primary-buttons {
- width: inherit;
-}
-
-.reports-link {
- display: block;
- text-align: center;
- margin: 0.45em 0 0;
- font-size: 0.9rem;
-}
-
-.git-string {
- font-style: italic;
- font-size: 0.9rem;
-}
-
-.test-plan {
- font-size: 1.1rem;
-}
-
-.btn.updater-button {
- width: initial;
-}
-
-.primary-test-run-select {
- &[size]:not([size='1']) {
- padding: 0 !important;
- }
-
- option {
- padding: 0.5rem;
- }
-}
diff --git a/client/components/TestQueueRow/index.jsx b/client/components/TestQueueRow/index.jsx
deleted file mode 100644
index 8584d58dc..000000000
--- a/client/components/TestQueueRow/index.jsx
+++ /dev/null
@@ -1,719 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-import PropTypes from 'prop-types';
-import { useApolloClient, useMutation } from '@apollo/client';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
-import nextId from 'react-id-generator';
-import { Form, Button, Dropdown } from 'react-bootstrap';
-import { Link } from 'react-router-dom';
-import {
- TEST_PLAN_REPORT_QUERY,
- ASSIGN_TESTER_MUTATION,
- MARK_TEST_PLAN_REPORT_AS_FINAL_MUTATION,
- REMOVE_TEST_PLAN_REPORT_MUTATION,
- REMOVE_TESTER_MUTATION,
- REMOVE_TESTER_RESULTS_MUTATION
-} from '../TestQueue/queries';
-import BasicModal from '../common/BasicModal';
-import BasicThemedModal from '../common/BasicThemedModal';
-import { LoadingStatus, useTriggerLoad } from '../common/LoadingStatus';
-import './TestQueueRow.css';
-import { useAriaLiveRegion } from '../providers/AriaLiveRegionProvider';
-import TestQueueCompletionStatusListItem from '../TestQueueCompletionStatusListItem';
-import AssignTesterDropdown from '../TestQueue/AssignTesterDropdown';
-import BotRunTestStatusList from '../BotRunTestStatusList';
-import ManageBotRunDialogWithButton from '../ManageBotRunDialog/WithButton';
-
-const TestQueueRow = ({
- user = {},
- testers = [],
- testPlanReportData = {},
- triggerDeleteTestPlanReportModal = () => {},
- triggerDeleteResultsModal = () => {},
- triggerPageUpdate = () => {}
-}) => {
- const client = useApolloClient();
- const { triggerLoad, loadingMessage } = useTriggerLoad();
-
- const focusButtonRef = useRef();
- const dropdownAssignTesterButtonRef = useRef();
- const assignTesterButtonRef = useRef();
- const dropdownDeleteTesterResultsButtonRef = useRef();
- const deleteTesterResultsButtonRef = useRef();
- const deleteTestPlanButtonRef = useRef();
- const updateTestPlanStatusButtonRef = useRef();
-
- const setAlertMessage = useAriaLiveRegion();
-
- const [showPrimaryTestPlanRunModal, setShowPrimaryTestPlanRunModal] =
- useState(false);
- const [showThemedModal, setShowThemedModal] = useState(false);
- const [themedModalType, setThemedModalType] = useState('warning');
- const [themedModalTitle, setThemedModalTitle] = useState('');
- const [themedModalContent, setThemedModalContent] = useState(<>>);
-
- const [assignTester] = useMutation(ASSIGN_TESTER_MUTATION);
- const [markTestPlanReportAsFinal] = useMutation(
- MARK_TEST_PLAN_REPORT_AS_FINAL_MUTATION
- );
- const [removeTestPlanReport] = useMutation(
- REMOVE_TEST_PLAN_REPORT_MUTATION
- );
- const [removeTester] = useMutation(REMOVE_TESTER_MUTATION);
- const [removeTesterResults] = useMutation(REMOVE_TESTER_RESULTS_MUTATION);
-
- const [testPlanReport, setTestPlanReport] = useState(testPlanReportData);
- const [primaryTestPlanRunId, setPrimaryTestPlanRunId] = useState(null);
- const [isLoading, setIsLoading] = useState(false);
-
- const { id, isAdmin, isTester, isVendor, username } = user;
- const {
- testPlanVersion,
- draftTestPlanRuns,
- runnableTestsLength = 0
- } = testPlanReport;
-
- const isSignedIn = !!id;
-
- useEffect(() => {
- setTestPlanReport(testPlanReportData);
- }, [testPlanReportData]);
-
- const checkIsTesterAssigned = username => {
- return draftTestPlanRuns.some(
- testPlanRun => testPlanRun.tester.username === username && isTester
- );
- };
-
- const currentUserAssigned = checkIsTesterAssigned(username);
- const currentUserTestPlanRun = currentUserAssigned
- ? draftTestPlanRuns.find(({ tester }) => tester.username === username)
- : {};
-
- const testPlanRunsWithResults = draftTestPlanRuns.filter(
- ({ testResultsLength = 0 }) => testResultsLength > 0
- );
-
- const primaryTestPlanRunOptions = draftTestPlanRuns
- .slice()
- .sort((a, b) =>
- a.tester.username.toLowerCase() < b.tester.username.toLowerCase()
- ? -1
- : 1
- )
- .map(run => ({
- testPlanRunId: run.id,
- ...run.tester
- }))
- .filter(tester => !tester.isBot);
-
- const getTestPlanRunIdByUserId = userId => {
- return draftTestPlanRuns.find(({ tester }) => tester.id === userId).id;
- };
-
- const triggerTestPlanReportUpdate = async (id = testPlanReport.id) => {
- setIsLoading(true);
- const { data } = await client.query({
- query: TEST_PLAN_REPORT_QUERY,
- variables: { testPlanReportId: id },
- fetchPolicy: 'network-only'
- });
- setTestPlanReport(data.testPlanReport);
- setIsLoading(false);
- };
-
- const toggleTesterAssign = async username => {
- const isTesterAssigned = checkIsTesterAssigned(username);
- const tester = testers.find(tester => tester.username === username);
-
- if (isTesterAssigned) {
- await triggerLoad(async () => {
- await removeTester({
- variables: {
- testReportId: testPlanReport.id,
- testerId: tester.id
- }
- });
- await triggerTestPlanReportUpdate();
- }, `Updating Test Plan Assignees. Deleting Test Plan Run for ${tester.username}`);
- } else {
- await triggerLoad(async () => {
- await assignTester({
- variables: {
- testReportId: testPlanReport.id,
- testerId: tester.id
- }
- });
- await triggerTestPlanReportUpdate();
- }, 'Updating Test Plan Assignees');
- }
-
- if (focusButtonRef.current) focusButtonRef.current.focus();
- };
-
- const handleRemoveTestPlanReport = async () => {
- await removeTestPlanReport({
- variables: {
- testReportId: testPlanReport.id
- }
- });
- await triggerPageUpdate();
- };
-
- const handleRemoveTesterResults = async testPlanRunId => {
- await removeTesterResults({
- variables: {
- testPlanRunId
- }
- });
- await triggerTestPlanReportUpdate();
- };
-
- const showThemedMessage = (title, content, theme) => {
- setThemedModalTitle(title);
- setThemedModalContent(content);
- setThemedModalType(theme);
- setShowThemedModal(true);
- };
-
- const onThemedModalClose = () => {
- setShowThemedModal(false);
- focusButtonRef.current.focus();
- };
-
- const renderAssignedUserToTestPlan = () => {
- const titleElement = (
- <>
- {testPlanVersion.title} {testPlanVersion.versionString}
- ({runnableTestsLength} Test
- {runnableTestsLength === 0 || runnableTestsLength > 1
- ? `s`
- : ''}
- )
- >
- );
-
- // Determine if current user is assigned to testPlan
- if (currentUserAssigned)
- return (
- <>
-
- {titleElement}
-
- >
- );
-
- if (!isSignedIn || (isVendor && !isAdmin))
- return (
- <>
-
- {titleElement}
-
- >
- );
-
- return {titleElement}
;
- };
-
- const renderAssignMenu = () => {
- return (
- {
- focusButtonRef.current =
- dropdownAssignTesterButtonRef.current;
- await triggerTestPlanReportUpdate();
- }}
- setAlertMessage={setAlertMessage}
- testPlanReportId={testPlanReport.id}
- possibleTesters={testers}
- draftTestPlanRuns={draftTestPlanRuns}
- dropdownAssignTesterButtonRef={dropdownAssignTesterButtonRef}
- />
- );
- };
-
- const evaluateTestPlanRunTitle = () => {
- const { title: apgExampleName, directory } = testPlanVersion;
- const testPlanTargetTitle = `${testPlanReport.at?.name} and ${testPlanReport.browser?.name}`;
-
- return `${apgExampleName || directory} for ${testPlanTargetTitle}`;
- };
-
- const renderOpenAsDropdown = () => {
- return (
-
-
- Open run as...
-
-
- {draftTestPlanRuns
- .slice() // because array was frozen
- .sort((a, b) =>
- a.tester.username.localeCompare(b.tester.username)
- )
- .map(({ tester }) => {
- return (
-
- {tester.username}
-
- );
- })}
-
-
- );
- };
-
- const renderDeleteMenu = () => {
- if (testPlanRunsWithResults.length) {
- return (
- <>
-
-
-
- Delete for...
-
-
- {testPlanRunsWithResults.map(({ id, tester }) => {
- return (
- {
- triggerDeleteResultsModal(
- evaluateTestPlanRunTitle(),
- tester.username,
- async () => {
- await triggerLoad(
- async () => {
- await handleRemoveTesterResults(
- id
- );
- },
- 'Removing Test Results'
- );
- dropdownDeleteTesterResultsButtonRef.current.focus();
- }
- );
- }}
- >
-
- {tester.username}
-
- );
- })}
-
-
- >
- );
- }
- };
-
- const renderSecondaryActions = () => {
- const botTestPlanRun = draftTestPlanRuns.find(
- ({ tester: { isBot } }) => isBot
- );
-
- if (isAdmin && !isLoading) {
- return (
- <>
- {botTestPlanRun && (
- {
- triggerDeleteResultsModal(
- evaluateTestPlanRunTitle(),
- botTestPlanRun.tester.username,
- async () => {
- await triggerLoad(async () => {
- await handleRemoveTesterResults(
- botTestPlanRun.id
- );
- }, 'Removing Test Results');
- dropdownDeleteTesterResultsButtonRef.current.focus();
- }
- );
- }}
- />
- )}
- {!testPlanReport.conflictsLength &&
- testPlanReport.draftTestPlanRuns.length > 0 &&
- testPlanReport.draftTestPlanRuns[0].testResultsLength >
- 0 &&
- completedAllTests && (
- {
- focusButtonRef.current =
- updateTestPlanStatusButtonRef.current;
-
- const primaryTestPlanRunId =
- primaryTestPlanRunOptions[0]
- .testPlanRunId;
-
- if (primaryTestPlanRunOptions.length > 1) {
- setPrimaryTestPlanRunId(
- primaryTestPlanRunId
- );
- setShowPrimaryTestPlanRunModal(true);
- } else {
- // Immediately mark as final with the
- // only option
- await updateReportMarkedFinal(
- primaryTestPlanRunId
- );
- }
- }}
- >
- Mark as Final
-
- )}
- {botTestPlanRun && (
-
- )}
- >
- );
- }
- };
-
- const handlePrimaryTestRunChange = e => {
- const value = e.target.value;
- setPrimaryTestPlanRunId(value);
- };
-
- const renderPrimaryRunSelectionDialog = testers => {
- return (
-
- When a tester's run is marked as primary, it means
- that their output for collected results will be
- prioritized and shown on report pages.
-
-
- A tester's run being marked as primary may also set
- the minimum required Assistive Technology Version that
- can be used for subsequent reports with that Test Plan
- Version and Assistive Technology combination.
-
-
-
- {testers.map(tester => (
-
- {tester.username}
-
- ))}
-
- >
- }
- closeLabel="Cancel"
- staticBackdrop={true}
- actions={[
- {
- label: 'Confirm',
- onClick: async () =>
- await updateReportMarkedFinal(primaryTestPlanRunId)
- }
- ]}
- useOnHide
- handleClose={() => {
- setPrimaryTestPlanRunId(null);
- setShowPrimaryTestPlanRunModal(false);
- }}
- />
- );
- };
-
- const updateReportMarkedFinal = async primaryTestPlanRunId => {
- try {
- await triggerLoad(async () => {
- await markTestPlanReportAsFinal({
- variables: {
- testReportId: testPlanReport.id,
- primaryTestPlanRunId
- }
- });
- await triggerPageUpdate();
- }, 'Updating Test Plan Status');
- } catch (e) {
- showThemedMessage(
- 'Error Updating Test Plan Status',
- <>{e.message}>,
- 'warning'
- );
- }
- };
-
- const evaluateLabelStatus = () => {
- const { conflictsLength } = testPlanReport;
-
- let labelStatus;
-
- if (isLoading) {
- labelStatus = (
- Loading ...
- );
- } else if (conflictsLength > 0) {
- let pluralizedStatus = `${conflictsLength} Conflict${
- conflictsLength === 1 ? '' : 's'
- }`;
- labelStatus = (
-
- {pluralizedStatus}
-
- );
- } else {
- labelStatus = (
- Draft
- );
- }
-
- return labelStatus;
- };
-
- const getRowId = tester =>
- [
- 'plan',
- testPlanReport.id,
- 'run',
- currentUserTestPlanRun.id,
- 'assignee',
- tester.username,
- 'completed'
- ].join('-');
-
- const completedAllTests = testPlanReport.draftTestPlanRuns.every(
- testPlanRun =>
- testPlanRun.testResultsLength === testPlanReport.runnableTestsLength
- );
-
- return (
-
-
- {renderAssignedUserToTestPlan()}
-
- {testPlanReport.minimumAtVersion
- ? `${testPlanReport.minimumAtVersion.name} or later`
- : testPlanReport.exactAtVersion.name}
-
-
- {isSignedIn && isTester && (
-
- {isAdmin && renderAssignMenu()}
-
- {
- focusButtonRef.current =
- assignTesterButtonRef.current;
- await toggleTesterAssign(username);
- }}
- className="assign-self"
- >
- {!currentUserAssigned
- ? 'Assign Yourself'
- : 'Delete your test plan run'}
-
-
-
- )}
-
- {draftTestPlanRuns.length !== 0 ? (
-
- {draftTestPlanRuns
- .slice() // because array was frozen
- .sort((a, b) =>
- a.tester.username < b.tester.username
- ? -1
- : 1
- )
- .map(draftTestPlanRun => (
-
- ))}
-
- ) : (
-
- No testers assigned
-
- )}
-
-
-
-
- {evaluateLabelStatus()}
-
- {isSignedIn && isTester && (
-
- {renderSecondaryActions()}
-
- )}
-
-
-
- {currentUserAssigned && (
-
- {currentUserTestPlanRun.testResultsLength > 0 &&
- currentUserTestPlanRun.testResultsLength <
- runnableTestsLength
- ? 'Continue testing'
- : 'Start testing'}
-
- )}
-
- {isAdmin && (
- {
- triggerDeleteTestPlanReportModal(
- testPlanReport.id,
- evaluateTestPlanRunTitle(),
- async () => {
- await triggerLoad(async () => {
- await handleRemoveTestPlanReport();
- }, 'Removing Test Plan');
- deleteTestPlanButtonRef.current.focus();
- }
- );
- }}
- >
- Remove
-
- )}
-
- {(!isSignedIn || (isVendor && !isAdmin)) && (
-
- View tests
-
- )}
-
- {isSignedIn && isTester && (
-
- {isAdmin && renderOpenAsDropdown()}
- {isAdmin && renderDeleteMenu()}
- {(!isAdmin &&
- currentUserTestPlanRun.testResultsLength >
- 0 && (
- {
- triggerDeleteResultsModal(
- evaluateTestPlanRunTitle(),
- username,
- async () => {
- await triggerLoad(
- async () => {
- await handleRemoveTesterResults(
- currentUserTestPlanRun.id
- );
- },
- 'Removing Test Results'
- );
- deleteTesterResultsButtonRef.current.focus();
- }
- );
- }}
- aria-label="Delete my results"
- >
-
- Delete Results
-
- )) ||
- null}
-
- )}
-
-
-
- {showThemedModal && (
-
- )}
- {showPrimaryTestPlanRunModal &&
- renderPrimaryRunSelectionDialog(primaryTestPlanRunOptions)}
-
- );
-};
-
-TestQueueRow.propTypes = {
- user: PropTypes.object,
- testers: PropTypes.array,
- isConflictsLoading: PropTypes.bool,
- testPlanReportData: PropTypes.object,
- latestTestPlanVersions: PropTypes.array,
- triggerDeleteTestPlanReportModal: PropTypes.func,
- triggerDeleteResultsModal: PropTypes.func,
- triggerPageUpdate: PropTypes.func
-};
-
-export default TestQueueRow;
diff --git a/client/components/TestRenderer/AssertionsFieldset/index.jsx b/client/components/TestRenderer/AssertionsFieldset/index.jsx
index 09d484b5d..902acc3ff 100644
--- a/client/components/TestRenderer/AssertionsFieldset/index.jsx
+++ b/client/components/TestRenderer/AssertionsFieldset/index.jsx
@@ -5,51 +5,51 @@ import styled from '@emotion/styled';
import supportJson from '../../../resources/support.json';
const Label = styled.label`
- display: block;
+ display: block;
- input {
- margin-right: 0.25rem;
- }
+ input {
+ margin-right: 0.25rem;
+ }
`;
const AssertionsFieldset = ({ assertions, commandIndex, assertionsHeader }) => {
- // Handle case where build process didn't include assertionResponseQuestion
- const normalizedHeader = useMemo(() => {
- return assertionsHeader?.descriptionHeader?.replace(
- 'undefined',
- supportJson.testPlanStrings.assertionResponseQuestion
- );
- }, [assertionsHeader]);
+ // Handle case where build process didn't include assertionResponseQuestion
+ const normalizedHeader = useMemo(() => {
+ return assertionsHeader?.descriptionHeader?.replace(
+ 'undefined',
+ supportJson.testPlanStrings.assertionResponseQuestion
+ );
+ }, [assertionsHeader]);
- return (
-
-
- {normalizedHeader}
-
- {assertions.map((assertion, assertionIndex) => {
- const { description, passed, click } = assertion;
+ return (
+
+
+ {normalizedHeader}
+
+ {assertions.map((assertion, assertionIndex) => {
+ const { description, passed, click } = assertion;
- return (
-
-
- {description[0]}
-
- );
- })}
-
- );
+ return (
+
+
+ {description[0]}
+
+ );
+ })}
+
+ );
};
AssertionsFieldset.propTypes = {
- assertions: PropTypes.array.isRequired,
- commandIndex: PropTypes.number.isRequired,
- assertionsHeader: PropTypes.object.isRequired
+ assertions: PropTypes.array.isRequired,
+ commandIndex: PropTypes.number.isRequired,
+ assertionsHeader: PropTypes.object.isRequired
};
export default AssertionsFieldset;
diff --git a/client/components/TestRenderer/OutputTextArea/index.jsx b/client/components/TestRenderer/OutputTextArea/index.jsx
index 4dee5f1a1..62b173ab3 100644
--- a/client/components/TestRenderer/OutputTextArea/index.jsx
+++ b/client/components/TestRenderer/OutputTextArea/index.jsx
@@ -6,107 +6,102 @@ import { Form } from 'react-bootstrap';
import { NO_OUTPUT_STRING } from './constants';
const OutputTextAreaWrapper = styled.div`
- > textarea {
- width: 100%;
- height: 3.5rem;
- }
+ > textarea {
+ width: 100%;
+ height: 3.5rem;
+ }
`;
const NoOutputCheckbox = styled(Form.Check)`
- display: inline-block;
- float: right;
- color: ${props => (props.disabled ? '#7F7F7F' : 'inherit')};
- > input {
- cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};
- margin-right: 4px;
- }
+ display: inline-block;
+ float: right;
+ color: ${props => (props.disabled ? '#7F7F7F' : 'inherit')};
+ > input {
+ cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};
+ margin-right: 4px;
+ }
`;
const OutputTextArea = ({
- commandIndex,
- atOutput,
- isSubmitted,
- readOnly = false
+ commandIndex,
+ atOutput,
+ isSubmitted,
+ readOnly = false
}) => {
- const [noOutput, setNoOutput] = useState(
- atOutput.value === NO_OUTPUT_STRING
- );
+ const [noOutput, setNoOutput] = useState(atOutput.value === NO_OUTPUT_STRING);
- const isMounted = useRef(false);
+ const isMounted = useRef(false);
- useEffect(() => {
- if (isMounted.current) {
- if (noOutput) {
- atOutput.change(NO_OUTPUT_STRING);
- } else {
- atOutput.change('');
- }
- } else {
- isMounted.current = true;
- }
- }, [noOutput]);
+ useEffect(() => {
+ if (isMounted.current) {
+ if (noOutput) {
+ atOutput.change(NO_OUTPUT_STRING);
+ } else {
+ atOutput.change('');
+ }
+ } else {
+ isMounted.current = true;
+ }
+ }, [noOutput]);
- useEffect(() => {
- setNoOutput(atOutput.value === NO_OUTPUT_STRING);
- }, [atOutput.value]);
+ useEffect(() => {
+ setNoOutput(atOutput.value === NO_OUTPUT_STRING);
+ }, [atOutput.value]);
- return (
-
-
- {atOutput.description[0]}
- {isSubmitted && (
-
- {atOutput.description[1].description}
-
- )}
-
- setNoOutput(!noOutput)}
- />
-
- );
+ return (
+
+
+ {atOutput.description[0]}
+ {isSubmitted && (
+
+ {atOutput.description[1].description}
+
+ )}
+
+ setNoOutput(!noOutput)}
+ />
+
+ );
};
OutputTextArea.propTypes = {
- commandIndex: PropTypes.number.isRequired,
- atOutput: PropTypes.shape({
- description: PropTypes.arrayOf(
- PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.shape({
- required: PropTypes.bool.isRequired,
- highlightRequired: PropTypes.bool.isRequired,
- description: PropTypes.string.isRequired
- })
- ])
- ).isRequired,
- value: PropTypes.string.isRequired,
- change: PropTypes.func.isRequired,
- focus: PropTypes.bool.isRequired
- }).isRequired,
- readOnly: PropTypes.bool,
- isSubmitted: PropTypes.bool.isRequired
+ commandIndex: PropTypes.number.isRequired,
+ atOutput: PropTypes.shape({
+ description: PropTypes.arrayOf(
+ PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.shape({
+ required: PropTypes.bool.isRequired,
+ highlightRequired: PropTypes.bool.isRequired,
+ description: PropTypes.string.isRequired
+ })
+ ])
+ ).isRequired,
+ value: PropTypes.string.isRequired,
+ change: PropTypes.func.isRequired,
+ focus: PropTypes.bool.isRequired
+ }).isRequired,
+ readOnly: PropTypes.bool,
+ isSubmitted: PropTypes.bool.isRequired
};
export default OutputTextArea;
diff --git a/client/components/TestRenderer/UnexpectedBehaviorsFieldset/index.jsx b/client/components/TestRenderer/UnexpectedBehaviorsFieldset/index.jsx
index 7601bc4f3..c6f9cf70e 100644
--- a/client/components/TestRenderer/UnexpectedBehaviorsFieldset/index.jsx
+++ b/client/components/TestRenderer/UnexpectedBehaviorsFieldset/index.jsx
@@ -4,228 +4,205 @@ import styled from '@emotion/styled';
import { Feedback, Fieldset } from '..';
const ProblemOptionContainer = styled.div`
- &.enabled {
- margin-bottom: 15px;
- }
+ &.enabled {
+ margin-bottom: 15px;
+ }
`;
const Label = styled.label`
- display: inline-block;
- width: 100%;
- margin-bottom: 5px;
+ display: inline-block;
+ width: 100%;
+ margin-bottom: 5px;
- > select,
- > input[type='text'] {
- min-width: 120px;
- width: 50%;
- height: 26px;
- margin-left: 5px;
- }
+ > select,
+ > input[type='text'] {
+ min-width: 120px;
+ width: 50%;
+ height: 26px;
+ margin-left: 5px;
+ }
- > input[type='checkbox'] {
- margin-right: 5px;
- }
+ > input[type='checkbox'] {
+ margin-right: 5px;
+ }
- &.off-screen {
- position: absolute !important;
- height: 1px;
- width: 1px;
- overflow: hidden;
- clip: rect(1px, 1px, 1px, 1px);
- white-space: nowrap;
- }
+ &.off-screen {
+ position: absolute !important;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+ clip: rect(1px, 1px, 1px, 1px);
+ white-space: nowrap;
+ }
`;
const UnexpectedBehaviorsFieldset = ({
- commandIndex,
- unexpectedBehaviors,
- isSubmitted
+ commandIndex,
+ unexpectedBehaviors,
+ isSubmitted
}) => {
- const impactOptions = ['Moderate', 'Severe'];
+ const impactOptions = ['Moderate', 'Severe'];
- return (
-
- {unexpectedBehaviors.description[0]}
- {isSubmitted && (
-
- {unexpectedBehaviors.description[1].description}
-
- )}
-
-
-
- {unexpectedBehaviors.passChoice.label}
-
-
-
-
-
- {unexpectedBehaviors.failChoice.label}
-
-
+ return (
+
+ {unexpectedBehaviors.description[0]}
+ {isSubmitted && (
+
+ {unexpectedBehaviors.description[1].description}
+
+ )}
+
+
+
+ {unexpectedBehaviors.passChoice.label}
+
+
+
+
+
+ {unexpectedBehaviors.failChoice.label}
+
+
-
- {unexpectedBehaviors.failChoice.options.header}
- {unexpectedBehaviors.failChoice.options.options.map(
- (option, optionIndex) => {
- const {
- checked,
- focus,
- description,
- more,
- impact,
- change,
- impactchange
- } = option;
+
+ {unexpectedBehaviors.failChoice.options.header}
+ {unexpectedBehaviors.failChoice.options.options.map(
+ (option, optionIndex) => {
+ const {
+ checked,
+ focus,
+ description,
+ more,
+ impact,
+ change,
+ impactchange
+ } = option;
- const descriptionId = description
- .toLowerCase()
- .replace(/[.,]/g, '')
- .replace(/\s+/g, '-');
+ const descriptionId = description
+ .toLowerCase()
+ .replace(/[.,]/g, '')
+ .replace(/\s+/g, '-');
- return (
-
- {/* Undesirable behavior checkbox */}
-
- change(e.target.checked)}
- />
- {description} behavior occurred
-
+ return (
+
+ {/* Undesirable behavior checkbox */}
+
+ change(e.target.checked)}
+ />
+ {description} behavior occurred
+
- {/* Impact select */}
-
- Impact:
-
- impactchange(e.target.value)
- }
- disabled={!checked}
- defaultValue={impact}
- >
- {impactOptions.map(option => (
-
- {option}
-
- ))}
-
-
+ {/* Impact select */}
+
+ Impact:
+ impactchange(e.target.value)}
+ disabled={!checked}
+ defaultValue={impact}
+ >
+ {impactOptions.map(option => (
+
+ {option}
+
+ ))}
+
+
- {/* Details text input */}
- {more && (
- <>
-
- Details:
-
- more.change(e.target.value)
- }
- disabled={!checked}
- />
- {isSubmitted && (
-
- {
- more.description[1]
- .description
- }
-
- )}
-
- >
- )}
-
- );
- }
+ {/* Details text input */}
+ {more && (
+ <>
+
+ Details:
+ more.change(e.target.value)}
+ disabled={!checked}
+ />
+ {isSubmitted && (
+
+ {more.description[1].description}
+
+ )}
+
+ >
)}
-
-
- );
+
+ );
+ }
+ )}
+
+
+ );
};
UnexpectedBehaviorsFieldset.propTypes = {
- commandIndex: PropTypes.number.isRequired,
- unexpectedBehaviors: PropTypes.object.isRequired,
- isSubmitted: PropTypes.bool
+ commandIndex: PropTypes.number.isRequired,
+ unexpectedBehaviors: PropTypes.object.isRequired,
+ isSubmitted: PropTypes.bool
};
export default UnexpectedBehaviorsFieldset;
diff --git a/client/components/TestRenderer/index.jsx b/client/components/TestRenderer/index.jsx
index 3570e656f..aac75c423 100644
--- a/client/components/TestRenderer/index.jsx
+++ b/client/components/TestRenderer/index.jsx
@@ -1,9 +1,9 @@
import React, {
- Fragment,
- useEffect,
- useLayoutEffect,
- useState,
- useRef
+ Fragment,
+ useEffect,
+ useLayoutEffect,
+ useState,
+ useRef
} from 'react';
import styled from '@emotion/styled';
import PropTypes from 'prop-types';
@@ -12,12 +12,12 @@ import { getMetrics } from 'shared';
import TestPlanResultsTable from '../common/TestPlanResultsTable';
import { parseListContent, parseSettingsContent } from './utils.js';
import {
- userCloseWindow,
- userOpenWindow
+ userCloseWindow,
+ userOpenWindow
} from '../../resources/aria-at-test-run.mjs';
import {
- TestRunExport,
- TestRunInputOutput
+ TestRunExport,
+ TestRunInputOutput
} from '../../resources/aria-at-test-io-format.mjs';
import { TestWindow } from '../../resources/aria-at-test-window.mjs';
import { evaluateAtNameKey } from '../../utils/aria';
@@ -28,17 +28,17 @@ import supportJson from '../../resources/support.json';
import commandsJson from '../../resources/commands.json';
const Container = styled.div`
- width: 100%;
+ width: 100%;
- border: black solid 2px;
- border-radius: 0.25rem;
- padding: 1rem;
+ border: black solid 2px;
+ border-radius: 0.25rem;
+ padding: 1rem;
- font-size: 14px;
+ font-size: 14px;
`;
const ErrorSection = styled.section`
- display: ${({ hasErrors }) => (hasErrors ? 'block' : 'none')};
+ display: ${({ hasErrors }) => (hasErrors ? 'block' : 'none')};
`;
const InstructionsSection = styled.section``;
@@ -46,91 +46,91 @@ const InstructionsSection = styled.section``;
const HeadingText = styled.h1``;
const SubHeadingText = styled.h2`
- instruction-list-heading {
- margin-top: 0;
- }
+ instruction-list-heading {
+ margin-top: 0;
+ }
`;
const InnerSectionHeadingText = styled.h3``;
const Text = styled.p`
- > textarea {
- width: 100%;
- }
+ > textarea {
+ width: 100%;
+ }
`;
export const Feedback = styled.span`
- &.required:not(.highlight-required) {
- display: none;
- }
-
- &.required.highlight-required {
- color: red;
- }
-
- &.off-screen {
- position: absolute !important;
- height: 1px;
- width: 1px;
- overflow: hidden;
- clip: rect(1px, 1px, 1px, 1px);
- white-space: nowrap;
- }
+ &.required:not(.highlight-required) {
+ display: none;
+ }
+
+ &.required.highlight-required {
+ color: red;
+ }
+
+ &.off-screen {
+ position: absolute !important;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+ clip: rect(1px, 1px, 1px, 1px);
+ white-space: nowrap;
+ }
`;
export const Fieldset = styled.fieldset`
+ display: block;
+ margin-inline-start: 2px;
+ margin-inline-end: 2px;
+ padding-block-start: 0.35em;
+ padding-inline-start: 0.75em;
+ padding-inline-end: 0.75em;
+ padding-block-end: 0.75em;
+
+ min-inline-size: min-content;
+
+ border-width: 2px;
+ border-style: groove;
+ border-color: threedface;
+ border-image: initial;
+
+ > legend {
display: block;
- margin-inline-start: 2px;
- margin-inline-end: 2px;
- padding-block-start: 0.35em;
- padding-inline-start: 0.75em;
- padding-inline-end: 0.75em;
- padding-block-end: 0.75em;
-
- min-inline-size: min-content;
-
- border-width: 2px;
- border-style: groove;
- border-color: threedface;
- border-image: initial;
- > legend {
- display: block;
+ float: inherit;
+ width: auto;
+ margin: 0;
+ font-size: initial;
- float: inherit;
- width: auto;
- margin: 0;
- font-size: initial;
+ padding-inline-start: 2px;
+ padding-inline-end: 2px;
+ border-width: initial;
+ border-style: none;
+ border-color: initial;
+ border-image: initial;
+ }
- padding-inline-start: 2px;
- padding-inline-end: 2px;
- border-width: initial;
- border-style: none;
- border-color: initial;
- border-image: initial;
+ > div {
+ > input[type='radio'] {
+ margin: 0 5px 0 0;
+ vertical-align: middle;
}
- > div {
- > input[type='radio'] {
- margin: 0 5px 0 0;
- vertical-align: middle;
- }
-
- > input[type='checkbox'] {
- margin: 0 5px 0 0;
- vertical-align: middle;
- }
+ > input[type='checkbox'] {
+ margin: 0 5px 0 0;
+ vertical-align: middle;
}
+ }
- &.problem-select {
- margin-top: 1em;
- margin-left: 1em;
+ &.problem-select {
+ margin-top: 1em;
+ margin-left: 1em;
- > input[type='checkbox'] {
- margin: 0 5px 0 0;
- vertical-align: middle;
- }
+ > input[type='checkbox'] {
+ margin: 0 5px 0 0;
+ vertical-align: middle;
}
+ }
`;
const NumberedList = styled.ol``;
@@ -140,493 +140,459 @@ const Button = styled.button``;
const ResultsSection = styled.section``;
const ErrorComponent = ({ hasErrors = false }) => {
- return (
-
- Test cannot be performed due to error(s)!
-
-
-
- );
+ return (
+
+ Test cannot be performed due to error(s)!
+
+
+
+ );
};
const TestRenderer = ({
- at,
- testResult = {},
- testPageUrl,
- testFormatVersion,
- testRunStateRef,
- recentTestRunStateRef,
- testRunResultRef,
- submitButtonRef,
- isSubmitted = false,
- isReviewingBot = false,
- isEdit = false,
- setIsRendererReady = false
+ at,
+ testResult = {},
+ testPageUrl,
+ testFormatVersion,
+ testRunStateRef,
+ recentTestRunStateRef,
+ testRunResultRef,
+ submitButtonRef,
+ isSubmitted = false,
+ isReviewingBot = false,
+ isEdit = false,
+ setIsRendererReady = false
}) => {
- const { scenarioResults, test = {}, completedAt } = testResult;
- const { renderableContent } = test;
-
- const mounted = useRef(false);
- const [testRunExport, setTestRunExport] = useState();
- const [pageContent, setPageContent] = useState(null);
- const [testRendererState, setTestRendererState] = useState(null);
- const [submitResult, setSubmitResult] = useState(null);
- const [submitCalled, setSubmitCalled] = useState(false);
-
- const setup = async () => {
- const testRunIO = new TestRunInputOutput();
-
- // Array.from(new URL(document.location).searchParams)
- const configQueryParams = [['at', evaluateAtNameKey(at.name)]];
-
- testRunIO.setAllCommandsInputFromJSON(commandsJson);
- await testRunIO.setInputsFromCollectedTestAsync(renderableContent);
- testRunIO.setConfigInputFromQueryParamsAndSupport(configQueryParams);
-
- if (renderableContent.target?.referencePage) {
- const replaceIndex = testPageUrl.indexOf('reference/');
- // sync with proxy url expected for aria-at-app to work properly
- const constructedTestPageUrl =
- testPageUrl.substring(0, replaceIndex) +
- renderableContent.target?.referencePage;
- testRunIO.setPageUriInputFromPageUri(constructedTestPageUrl);
- } else testRunIO.setPageUriInputFromPageUri(testPageUrl);
-
- const _state = remapState(testRunIO.testRunState(), scenarioResults);
-
- const testWindow = new TestWindow({
- ...testRunIO.testWindowOptions(),
- hooks: {
- windowOpened() {
- testRunExport.dispatch(userOpenWindow());
- },
- windowClosed() {
- testRunExport.dispatch(userCloseWindow());
- }
- }
- });
+ const { scenarioResults, test = {}, completedAt } = testResult;
+ const { renderableContent } = test;
+
+ const mounted = useRef(false);
+ const [testRunExport, setTestRunExport] = useState();
+ const [pageContent, setPageContent] = useState(null);
+ const [testRendererState, setTestRendererState] = useState(null);
+ const [submitResult, setSubmitResult] = useState(null);
+ const [submitCalled, setSubmitCalled] = useState(false);
+
+ const setup = async () => {
+ const testRunIO = new TestRunInputOutput();
+
+ // Array.from(new URL(document.location).searchParams)
+ const configQueryParams = [['at', evaluateAtNameKey(at.name)]];
+
+ testRunIO.setAllCommandsInputFromJSON(commandsJson);
+ await testRunIO.setInputsFromCollectedTestAsync(renderableContent);
+ testRunIO.setConfigInputFromQueryParamsAndSupport(configQueryParams);
+
+ if (renderableContent.target?.referencePage) {
+ const replaceIndex = testPageUrl.indexOf('reference/');
+ // sync with proxy url expected for aria-at-app to work properly
+ const constructedTestPageUrl =
+ testPageUrl.substring(0, replaceIndex) +
+ renderableContent.target?.referencePage;
+ testRunIO.setPageUriInputFromPageUri(constructedTestPageUrl);
+ } else testRunIO.setPageUriInputFromPageUri(testPageUrl);
+
+ const _state = remapState(testRunIO.testRunState(), scenarioResults);
+
+ const testWindow = new TestWindow({
+ ...testRunIO.testWindowOptions(),
+ hooks: {
+ windowOpened() {
+ testRunExport.dispatch(userOpenWindow());
+ },
+ windowClosed() {
+ testRunExport.dispatch(userCloseWindow());
+ }
+ }
+ });
+
+ const testRunExport = new TestRunExport({
+ hooks: {
+ openTestPage() {
+ testWindow.open();
+ },
+ closeTestPage() {
+ testWindow.close();
+ }
+ },
+ resultsJSON: state => testRunIO.submitResultsJSON(state),
+ state: _state
+ });
+ mounted.current && setTestRendererState(_state);
+ mounted.current && setTestRunExport(testRunExport);
+ };
+
+ const remapState = (state, scenarioResults = []) => {
+ const { commands } = state;
+
+ if (!scenarioResults.length || commands.length !== scenarioResults.length) {
+ return state;
+ }
- const testRunExport = new TestRunExport({
- hooks: {
- openTestPage() {
- testWindow.open();
- },
- closeTestPage() {
- testWindow.close();
- }
- },
- resultsJSON: state => testRunIO.submitResultsJSON(state),
- state: _state
- });
- mounted.current && setTestRendererState(_state);
- mounted.current && setTestRunExport(testRunExport);
+ const UnexpectedBehaviorsMap = {
+ EXCESSIVELY_VERBOSE: 0,
+ UNEXPECTED_CURSOR_POSITION: 1,
+ SLUGGISH: 2,
+ AT_CRASHED: 3,
+ BROWSER_CRASHED: 4,
+ OTHER: 5
};
- const remapState = (state, scenarioResults = []) => {
- const { commands } = state;
+ for (let i = 0; i < scenarioResults.length; i++) {
+ let {
+ output,
+ assertionResults,
+ unexpectedBehaviors,
+ highlightRequired = false, // atOutput
+ unexpectedBehaviorHighlightRequired = false
+ } = scenarioResults[i];
- if (
- !scenarioResults.length ||
- commands.length !== scenarioResults.length
- ) {
- return state;
- }
+ if (output) commands[i].atOutput.value = output;
+ commands[i].atOutput.highlightRequired = highlightRequired;
- const UnexpectedBehaviorsMap = {
- EXCESSIVELY_VERBOSE: 0,
- UNEXPECTED_CURSOR_POSITION: 1,
- SLUGGISH: 2,
- AT_CRASHED: 3,
- BROWSER_CRASHED: 4,
- OTHER: 5
- };
-
- for (let i = 0; i < scenarioResults.length; i++) {
- let {
- output,
- assertionResults,
- unexpectedBehaviors,
- highlightRequired = false, // atOutput
- unexpectedBehaviorHighlightRequired = false
- } = scenarioResults[i];
-
- if (output) commands[i].atOutput.value = output;
- commands[i].atOutput.highlightRequired = highlightRequired;
-
- // Required because assertionResults can now be returned without an id if there is a 0-priority exception
- // applied
- assertionResults = assertionResults.filter(el => !!el.id);
-
- for (let j = 0; j < assertionResults.length; j++) {
- const { passed, highlightRequired, assertion } =
- assertionResults[j];
-
- let assertionForCommandIndex = commands[i].assertions.findIndex(
- ({ description }) => description === assertion?.text
- );
- commands[i].assertions[assertionForCommandIndex].result = passed
- ? 'pass'
- : 'fail';
- commands[i].assertions[
- assertionForCommandIndex
- ].highlightRequired = highlightRequired;
- }
-
- if (unexpectedBehaviors && unexpectedBehaviors.length) {
- commands[i].unexpected.hasUnexpected = 'hasUnexpected';
-
- for (let k = 0; k < unexpectedBehaviors.length; k++) {
- /**
- * 0 = EXCESSIVELY_VERBOSE
- * 1 = UNEXPECTED_CURSOR_POSITION
- * 2 = SLUGGISH
- * 3 = AT_CRASHED
- * 4 = BROWSER_CRASHED
- * 5 = OTHER
- */
-
- const { id, details, impact, highlightRequired } =
- unexpectedBehaviors[k];
-
- // Capture positional index of unexpected behavior based on id
- const index = UnexpectedBehaviorsMap[id];
-
- commands[i].unexpected.behaviors[index].checked = true;
- commands[i].unexpected.behaviors[index].more.value =
- details;
- commands[i].unexpected.behaviors[index].impact =
- impact.toUpperCase();
- commands[i].unexpected.behaviors[
- index
- ].more.highlightRequired = highlightRequired;
- }
- } else if (unexpectedBehaviors)
- // but not populated
- commands[i].unexpected.hasUnexpected = 'doesNotHaveUnexpected';
- else commands[i].unexpected.hasUnexpected = 'notSet';
-
- commands[i].unexpected.highlightRequired =
- unexpectedBehaviorHighlightRequired;
- }
+ // Required because assertionResults can now be returned without an id if there is a 0-priority exception
+ // applied
+ assertionResults = assertionResults.filter(el => !!el.id);
- return { ...state, commands, currentUserAction: 'validateResults' };
- };
+ for (let j = 0; j < assertionResults.length; j++) {
+ const { passed, highlightRequired, assertion } = assertionResults[j];
- useEffect(() => {
- (async () => {
- mounted.current = true;
- await setup();
- })();
- return () => {
- mounted.current = false;
- };
- }, []);
-
- useLayoutEffect(() => {
- if (testRunExport) {
- testRunExport.observe(result => {
- const { state: newState } = result;
- const pageContent = testRunExport.instructions();
- const submitResult = testRunExport.testPageAndResults();
-
- setPageContent(pageContent);
- setSubmitResult(submitResult);
-
- testRunStateRef.current = newState;
- recentTestRunStateRef.current = newState;
- testRunResultRef.current =
- submitResult &&
- submitResult.resultsJSON &&
- submitResult.results
- ? submitResult
- : null;
- });
-
- setPageContent(testRunExport.instructions());
+ let assertionForCommandIndex = commands[i].assertions.findIndex(
+ ({ description }) => description === assertion?.text
+ );
+ commands[i].assertions[assertionForCommandIndex].result = passed
+ ? 'pass'
+ : 'fail';
+ commands[i].assertions[assertionForCommandIndex].highlightRequired =
+ highlightRequired;
+ }
+
+ if (unexpectedBehaviors && unexpectedBehaviors.length) {
+ commands[i].unexpected.hasUnexpected = 'hasUnexpected';
+
+ for (let k = 0; k < unexpectedBehaviors.length; k++) {
+ /**
+ * 0 = EXCESSIVELY_VERBOSE
+ * 1 = UNEXPECTED_CURSOR_POSITION
+ * 2 = SLUGGISH
+ * 3 = AT_CRASHED
+ * 4 = BROWSER_CRASHED
+ * 5 = OTHER
+ */
+
+ const { id, details, impact, highlightRequired } =
+ unexpectedBehaviors[k];
+
+ // Capture positional index of unexpected behavior based on id
+ const index = UnexpectedBehaviorsMap[id];
+
+ commands[i].unexpected.behaviors[index].checked = true;
+ commands[i].unexpected.behaviors[index].more.value = details;
+ commands[i].unexpected.behaviors[index].impact = impact.toUpperCase();
+ commands[i].unexpected.behaviors[index].more.highlightRequired =
+ highlightRequired;
}
+ } else if (unexpectedBehaviors)
+ // but not populated
+ commands[i].unexpected.hasUnexpected = 'doesNotHaveUnexpected';
+ else commands[i].unexpected.hasUnexpected = 'notSet';
- testRunStateRef.current = testRendererState;
- recentTestRunStateRef.current = testRendererState;
- setIsRendererReady(true);
- }, [testRunExport]);
-
- useEffect(() => {
- if (!submitCalled && completedAt && pageContent) {
- testRunStateRef.current = testRendererState;
- recentTestRunStateRef.current = testRendererState;
- pageContent.submit.click();
- setSubmitCalled(true);
- }
- return () => {
- setSubmitCalled(false);
-
- // Use to validate whether errors exist on the page. Error
- // feedback may be erased on submit otherwise
- if (
- !checkStateForErrors(testRunStateRef.current) &&
- !checkPageContentForErrors(pageContent)
- )
- testRunStateRef.current = null;
- };
- }, [pageContent]);
-
- const checkStateForErrors = state => {
- if (state) {
- const { commands } = state;
- // short circuit all checks
- return commands.some(item => {
- const atOutputError = item.atOutput.highlightRequired;
- if (atOutputError) return true;
-
- const unexpectedError = item.unexpected.highlightRequired;
- if (unexpectedError) return true;
-
- const { behaviors } = item.unexpected;
- const uncheckedBehaviorsMoreError = behaviors.some(item => {
- if (item.more) return item.more.highlightRequired;
- return false;
- });
- if (uncheckedBehaviorsMoreError) return true;
- return false;
- });
- }
- return false;
- };
+ commands[i].unexpected.highlightRequired =
+ unexpectedBehaviorHighlightRequired;
+ }
- const checkPageContentForErrors = pageContent => {
- if (pageContent) {
- const { commands } = pageContent.results;
- // short circuit all checks
- return commands.some(item => {
- const atOutputError =
- item.atOutput.description[1].highlightRequired;
- if (atOutputError) return true;
-
- const unexpectedBehaviorError =
- item.unexpectedBehaviors.description[1].highlightRequired;
- if (unexpectedBehaviorError) return true;
-
- const { failChoice } = item.unexpectedBehaviors;
- const failChoiceOptionsMoreError =
- failChoice.options.options.some(item => {
- if (item.more)
- return item.more.description[1].highlightRequired;
- else return false;
- });
- if (failChoiceOptionsMoreError) return true;
- return false;
- });
- }
- return false;
- };
+ return { ...state, commands, currentUserAction: 'validateResults' };
+ };
- const InstructionsContent = ({ labelIdRef }) => {
- let allInstructions;
- const isV2 = testFormatVersion === 2;
- let settingsContent = [];
-
- if (isV2) {
- // There is at least one defined 'setting' for the list of AT commands
- const commandSettingSpecified = renderableContent.commands.some(
- ({ settings }) => settings && settings !== 'defaultMode'
- );
-
- const defaultInstructions =
- renderableContent.target.at.raw
- .defaultConfigurationInstructionsHTML;
- const setupScriptDescription = `${supportJson.testPlanStrings.openExampleInstruction} ${renderableContent.target.setupScript.scriptDescription}`;
- const testInstructions =
- renderableContent.instructions.instructions;
- const settingsInstructions = `${
- supportJson.testPlanStrings.commandListPreface
- }${
- commandSettingSpecified
- ? ` ${supportJson.testPlanStrings.commandListSettingsPreface}`
- : ''
- }`;
-
- allInstructions = [
- defaultInstructions,
- setupScriptDescription + '.',
- testInstructions + ' ' + settingsInstructions
- ].map(e => unescape(e));
- settingsContent = parseSettingsContent(
- renderableContent.instructions.mode,
- renderableContent.target.at.raw.settings
- );
- } else {
- allInstructions = [
- ...pageContent.instructions.instructions.instructions,
- ...pageContent.instructions.instructions.strongInstructions,
- pageContent.instructions.instructions.commands.description
- ];
- }
+ useEffect(() => {
+ (async () => {
+ mounted.current = true;
+ await setup();
+ })();
+ return () => {
+ mounted.current = false;
+ };
+ }, []);
+
+ useLayoutEffect(() => {
+ if (testRunExport) {
+ testRunExport.observe(result => {
+ const { state: newState } = result;
+ const pageContent = testRunExport.instructions();
+ const submitResult = testRunExport.testPageAndResults();
+
+ setPageContent(pageContent);
+ setSubmitResult(submitResult);
+
+ testRunStateRef.current = newState;
+ recentTestRunStateRef.current = newState;
+ testRunResultRef.current =
+ submitResult && submitResult.resultsJSON && submitResult.results
+ ? submitResult
+ : null;
+ });
+
+ setPageContent(testRunExport.instructions());
+ }
- const commands =
- pageContent.instructions.instructions.commands.commands;
- const commandsContent = parseListContent(commands);
- const content = parseListContent(allInstructions, commandsContent);
-
- return (
- <>
-
- {content}
-
- {settingsContent.length ? settingsContent : null}
- >
- );
+ testRunStateRef.current = testRendererState;
+ recentTestRunStateRef.current = testRendererState;
+ setIsRendererReady(true);
+ }, [testRunExport]);
+
+ useEffect(() => {
+ if (!submitCalled && completedAt && pageContent) {
+ testRunStateRef.current = testRendererState;
+ recentTestRunStateRef.current = testRendererState;
+ pageContent.submit.click();
+ setSubmitCalled(true);
+ }
+ return () => {
+ setSubmitCalled(false);
+
+ // Use to validate whether errors exist on the page. Error
+ // feedback may be erased on submit otherwise
+ if (
+ !checkStateForErrors(testRunStateRef.current) &&
+ !checkPageContentForErrors(pageContent)
+ )
+ testRunStateRef.current = null;
};
+ }, [pageContent]);
+
+ const checkStateForErrors = state => {
+ if (state) {
+ const { commands } = state;
+ // short circuit all checks
+ return commands.some(item => {
+ const atOutputError = item.atOutput.highlightRequired;
+ if (atOutputError) return true;
+
+ const unexpectedError = item.unexpected.highlightRequired;
+ if (unexpectedError) return true;
+
+ const { behaviors } = item.unexpected;
+ const uncheckedBehaviorsMoreError = behaviors.some(item => {
+ if (item.more) return item.more.highlightRequired;
+ return false;
+ });
+ if (uncheckedBehaviorsMoreError) return true;
+ return false;
+ });
+ }
+ return false;
+ };
+
+ const checkPageContentForErrors = pageContent => {
+ if (pageContent) {
+ const { commands } = pageContent.results;
+ // short circuit all checks
+ return commands.some(item => {
+ const atOutputError = item.atOutput.description[1].highlightRequired;
+ if (atOutputError) return true;
+
+ const unexpectedBehaviorError =
+ item.unexpectedBehaviors.description[1].highlightRequired;
+ if (unexpectedBehaviorError) return true;
+
+ const { failChoice } = item.unexpectedBehaviors;
+ const failChoiceOptionsMoreError = failChoice.options.options.some(
+ item => {
+ if (item.more) return item.more.description[1].highlightRequired;
+ else return false;
+ }
+ );
+ if (failChoiceOptionsMoreError) return true;
+ return false;
+ });
+ }
+ return false;
+ };
+
+ const InstructionsContent = ({ labelIdRef }) => {
+ let allInstructions;
+ const isV2 = testFormatVersion === 2;
+ let settingsContent = [];
+
+ if (isV2) {
+ // There is at least one defined 'setting' for the list of AT commands
+ const commandSettingSpecified = renderableContent.commands.some(
+ ({ settings }) => settings && settings !== 'defaultMode'
+ );
+
+ const defaultInstructions =
+ renderableContent.target.at.raw.defaultConfigurationInstructionsHTML;
+ const setupScriptDescription = `${supportJson.testPlanStrings.openExampleInstruction} ${renderableContent.target.setupScript.scriptDescription}`;
+ const testInstructions = renderableContent.instructions.instructions;
+ const settingsInstructions = `${
+ supportJson.testPlanStrings.commandListPreface
+ }${
+ commandSettingSpecified
+ ? ` ${supportJson.testPlanStrings.commandListSettingsPreface}`
+ : ''
+ }`;
+
+ allInstructions = [
+ defaultInstructions,
+ setupScriptDescription + '.',
+ testInstructions + ' ' + settingsInstructions
+ ].map(e => unescape(e));
+ settingsContent = parseSettingsContent(
+ renderableContent.instructions.mode,
+ renderableContent.target.at.raw.settings
+ );
+ } else {
+ allInstructions = [
+ ...pageContent.instructions.instructions.instructions,
+ ...pageContent.instructions.instructions.strongInstructions,
+ pageContent.instructions.instructions.commands.description
+ ];
+ }
- InstructionsContent.propTypes = { labelIdRef: PropTypes.string };
+ const commands = pageContent.instructions.instructions.commands.commands;
+ const commandsContent = parseListContent(commands);
+ const content = parseListContent(allInstructions, commandsContent);
- const AssertionsContent = ({ labelIdRef }) => {
- const assertions = [...pageContent.instructions.assertions.assertions];
- const content = parseListContent(assertions);
+ return (
+ <>
+ {content}
+ {settingsContent.length ? settingsContent : null}
+ >
+ );
+ };
- return (
- {content}
- );
- };
+ InstructionsContent.propTypes = { labelIdRef: PropTypes.string };
- AssertionsContent.propTypes = { labelIdRef: PropTypes.string };
+ const AssertionsContent = ({ labelIdRef }) => {
+ const assertions = [...pageContent.instructions.assertions.assertions];
+ const content = parseListContent(assertions);
- const SubmitResultsContent = () => {
- const { results } = submitResult;
- const { header } = results;
+ return {content} ;
+ };
- const { assertionsPassedCount, assertionsFailedCount } = getMetrics({
- testResult
- });
+ AssertionsContent.propTypes = { labelIdRef: PropTypes.string };
- return (
- <>
- {header}
-
- Test Results (
- {assertionsPassedCount} passed,
- {assertionsFailedCount} failed)
-
-
- >
- );
- };
+ const SubmitResultsContent = () => {
+ const { results } = submitResult;
+ const { header } = results;
- // pageContent to render instructions; submitResult.resultsJSON indicates results have been submitted
- if (!pageContent) return null;
+ const { assertionsPassedCount, assertionsFailedCount } = getMetrics({
+ testResult
+ });
return (
-
- {!isEdit &&
- submitResult &&
- submitResult.resultsJSON &&
- submitResult.results ? (
-
- ) : (
- <>
-
-
-
- Instructions
-
-
-
- {pageContent.instructions.openTestPage.button}
-
-
-
-
- {pageContent.results.header.header}
-
- {pageContent.results.header.description}
- {pageContent.results.commands.map(
- (value, commandIndex) => {
- const {
- header,
- atOutput,
- assertions,
- unexpectedBehaviors,
- assertionsHeader
- } = value;
- return (
-
-
- {header}
-
-
-
-
-
- );
- }
- )}
-
- {
- testRunStateRef.current = testRendererState;
- recentTestRunStateRef.current = testRendererState;
- pageContent.submit.click();
- }}
- >
- {pageContent.submit.button}
-
- >
- )}
-
+ <>
+ {header}
+
+ Test Results (
+ {assertionsPassedCount} passed,
+ {assertionsFailedCount} failed)
+
+
+ >
);
+ };
+
+ // pageContent to render instructions; submitResult.resultsJSON indicates results have been submitted
+ if (!pageContent) return null;
+
+ return (
+
+ {!isEdit &&
+ submitResult &&
+ submitResult.resultsJSON &&
+ submitResult.results ? (
+
+ ) : (
+ <>
+
+
+
+ Instructions
+
+
+
+ {pageContent.instructions.openTestPage.button}
+
+
+
+ {pageContent.results.header.header}
+ {pageContent.results.header.description}
+ {pageContent.results.commands.map((value, commandIndex) => {
+ const {
+ header,
+ atOutput,
+ assertions,
+ unexpectedBehaviors,
+ assertionsHeader
+ } = value;
+ return (
+
+ {header}
+
+
+
+
+ );
+ })}
+
+ {
+ testRunStateRef.current = testRendererState;
+ recentTestRunStateRef.current = testRendererState;
+ pageContent.submit.click();
+ }}
+ >
+ {pageContent.submit.button}
+
+ >
+ )}
+
+ );
};
ErrorComponent.propTypes = {
- hasErrors: PropTypes.bool
+ hasErrors: PropTypes.bool
};
TestRenderer.propTypes = {
- at: PropTypes.object,
- testResult: PropTypes.object,
- support: PropTypes.object,
- testPageUrl: PropTypes.string,
- testFormatVersion: PropTypes.number,
- testRunStateRef: PropTypes.any,
- recentTestRunStateRef: PropTypes.any,
- testRunResultRef: PropTypes.any,
- submitButtonRef: PropTypes.any,
- isSubmitted: PropTypes.bool,
- isEdit: PropTypes.bool,
- isReviewingBot: PropTypes.bool,
- setIsRendererReady: PropTypes.func
+ at: PropTypes.object,
+ testResult: PropTypes.object,
+ support: PropTypes.object,
+ testPageUrl: PropTypes.string,
+ testFormatVersion: PropTypes.number,
+ testRunStateRef: PropTypes.any,
+ recentTestRunStateRef: PropTypes.any,
+ testRunResultRef: PropTypes.any,
+ submitButtonRef: PropTypes.any,
+ isSubmitted: PropTypes.bool,
+ isEdit: PropTypes.bool,
+ isReviewingBot: PropTypes.bool,
+ setIsRendererReady: PropTypes.func
};
export default TestRenderer;
diff --git a/client/components/TestRenderer/utils.js b/client/components/TestRenderer/utils.js
index 97fc35ff5..19ab8495a 100644
--- a/client/components/TestRenderer/utils.js
+++ b/client/components/TestRenderer/utils.js
@@ -6,94 +6,94 @@ import supportJson from '@client/resources/support.json';
import { unescape } from 'lodash';
const BulletList = styled.ul`
- padding-inline-start: 2.5rem;
- list-style-type: circle;
+ padding-inline-start: 2.5rem;
+ list-style-type: circle;
- > li {
- display: list-item;
- list-style: circle;
- }
+ > li {
+ display: list-item;
+ list-style: circle;
+ }
`;
const parseRichContentFromArray = (instruction = []) => {
- let content = null;
- for (let value of instruction) {
- if (typeof value === 'string') {
- if (value === '.')
- content = (
- <>
- {content}
- {value}
- >
- );
- else
- content = (
- <>
- {content} {value}
- >
- );
- } else if ('href' in value) {
- const { href, description } = value;
- content = (
- <>
- {content} {description}
- >
- );
- }
+ let content = null;
+ for (let value of instruction) {
+ if (typeof value === 'string') {
+ if (value === '.')
+ content = (
+ <>
+ {content}
+ {value}
+ >
+ );
+ else
+ content = (
+ <>
+ {content} {value}
+ >
+ );
+ } else if ('href' in value) {
+ const { href, description } = value;
+ content = (
+ <>
+ {content} {description}
+ >
+ );
}
- return content;
+ }
+ return content;
};
const parseListContent = (instructions = [], commandsContent = null) => {
- return instructions.map((value, index) => {
- if (typeof value === 'string')
- return (
-
- {ReactHtmlParser(value)}
- {commandsContent && index === instructions.length - 1 && (
- {commandsContent}
- )}
-
- );
- else if (Array.isArray(value))
- return (
-
- {parseRichContentFromArray(value)}
- {commandsContent && index === instructions.length - 1 && (
- {commandsContent}
- )}
-
- );
- });
+ return instructions.map((value, index) => {
+ if (typeof value === 'string')
+ return (
+
+ {ReactHtmlParser(value)}
+ {commandsContent && index === instructions.length - 1 && (
+ {commandsContent}
+ )}
+
+ );
+ else if (Array.isArray(value))
+ return (
+
+ {parseRichContentFromArray(value)}
+ {commandsContent && index === instructions.length - 1 && (
+ {commandsContent}
+ )}
+
+ );
+ });
};
const parseSettingsContent = (settings, settingsMap) => {
- let settingsContent = [];
- Object.keys(settings).forEach(key => {
- if (key !== 'defaultMode') {
- const settingInstructions = settings[key];
- const text = `${supportJson.testPlanStrings.settingInstructionsPreface} ${settingsMap[key].screenText}:`;
- const instructions = settingInstructions
- .slice(1)
- .map(instruction => unescape(instruction));
+ let settingsContent = [];
+ Object.keys(settings).forEach(key => {
+ if (key !== 'defaultMode') {
+ const settingInstructions = settings[key];
+ const text = `${supportJson.testPlanStrings.settingInstructionsPreface} ${settingsMap[key].screenText}:`;
+ const instructions = settingInstructions
+ .slice(1)
+ .map(instruction => unescape(instruction));
- settingsContent.push(
-
- {text}
-
- {instructions.map((instruction, index) => {
- return (
-
- {ReactHtmlParser(instruction)}
-
- );
- })}
-
-
- );
- }
- });
- return settingsContent;
+ settingsContent.push(
+
+ {text}
+
+ {instructions.map((instruction, index) => {
+ return (
+
+ {ReactHtmlParser(instruction)}
+
+ );
+ })}
+
+
+ );
+ }
+ });
+ return settingsContent;
};
export { parseListContent, parseSettingsContent };
diff --git a/client/components/TestReview/index.jsx b/client/components/TestReview/index.jsx
index 60fe72e01..783669bbb 100644
--- a/client/components/TestReview/index.jsx
+++ b/client/components/TestReview/index.jsx
@@ -13,330 +13,304 @@ import { convertDateToString } from '../../utils/formatter';
import supportJson from '../../resources/support.json';
const Ul = styled.ul`
- li {
- list-style-type: disc;
- margin-left: 20px;
- }
+ li {
+ list-style-type: disc;
+ margin-left: 20px;
+ }
`;
const FilterButtonContainer = styled.div`
- padding: 0 0.75rem;
- border: 1px solid #d2d5d9;
- border-radius: 3px;
- margin-bottom: 40px;
+ padding: 0 0.75rem;
+ border: 1px solid #d2d5d9;
+ border-radius: 3px;
+ margin-bottom: 40px;
`;
const TestReview = () => {
- const { testPlanVersionId } = useParams();
+ const { testPlanVersionId } = useParams();
- const { loading, data, error } = useQuery(TEST_REVIEW_PAGE_QUERY, {
- variables: { testPlanVersionId },
- fetchPolicy: 'cache-and-network'
- });
- const [activeFilter, setActiveFilter] = useState('All ATs');
-
- if (loading) {
- return (
-
- );
- }
- if (error || !data?.testPlanVersion) {
- const errorMessage =
- error?.message ??
- `Failed to find a test plan version with ID ${testPlanVersionId}`;
- return (
-
- );
- }
- const testPlanVersion = data.testPlanVersion;
- const atNames = supportJson.ats.map(at => at.name);
- const isV2 = testPlanVersion.metadata.testFormatVersion === 2;
+ const { loading, data, error } = useQuery(TEST_REVIEW_PAGE_QUERY, {
+ variables: { testPlanVersionId },
+ fetchPolicy: 'cache-and-network'
+ });
+ const [activeFilter, setActiveFilter] = useState('All ATs');
- let testPlanVersionTests;
- if (isV2) {
- testPlanVersionTests = [];
- testPlanVersion.tests.forEach(test => {
- const testIndex = testPlanVersionTests.findIndex(
- el => el.title === test.title
- );
+ if (loading) {
+ return (
+
+ );
+ }
+ if (error || !data?.testPlanVersion) {
+ const errorMessage =
+ error?.message ??
+ `Failed to find a test plan version with ID ${testPlanVersionId}`;
+ return (
+
+ );
+ }
+ const testPlanVersion = data.testPlanVersion;
+ const atNames = supportJson.ats.map(at => at.name);
+ const isV2 = testPlanVersion.metadata.testFormatVersion === 2;
- if (testIndex < 0) testPlanVersionTests.push(test);
- else {
- testPlanVersionTests[testIndex] = {
- ...testPlanVersionTests[testIndex],
- id: `${testPlanVersionTests[testIndex].id}${test.id}`,
- ats: [...testPlanVersionTests[testIndex].ats, ...test.ats],
- renderableContents: [
- ...testPlanVersionTests[testIndex].renderableContents,
- ...test.renderableContents
- ],
- renderedUrls: [
- ...testPlanVersionTests[testIndex].renderedUrls,
- ...test.renderedUrls
- ]
- };
- }
- });
- } else {
- testPlanVersionTests = testPlanVersion.tests;
- }
+ let testPlanVersionTests;
+ if (isV2) {
+ testPlanVersionTests = [];
+ testPlanVersion.tests.forEach(test => {
+ const testIndex = testPlanVersionTests.findIndex(
+ el => el.title === test.title
+ );
- let filteredTests;
- if (activeFilter === 'All ATs') {
- filteredTests = testPlanVersionTests;
- } else {
- filteredTests = testPlanVersionTests.filter(test =>
- test.ats.find(at => at.name === activeFilter)
- );
- }
+ if (testIndex < 0) testPlanVersionTests.push(test);
+ else {
+ testPlanVersionTests[testIndex] = {
+ ...testPlanVersionTests[testIndex],
+ id: `${testPlanVersionTests[testIndex].id}${test.id}`,
+ ats: [...testPlanVersionTests[testIndex].ats, ...test.ats],
+ renderableContents: [
+ ...testPlanVersionTests[testIndex].renderableContents,
+ ...test.renderableContents
+ ],
+ renderedUrls: [
+ ...testPlanVersionTests[testIndex].renderedUrls,
+ ...test.renderedUrls
+ ]
+ };
+ }
+ });
+ } else {
+ testPlanVersionTests = testPlanVersion.tests;
+ }
- const filterOptions = Object.fromEntries(
- ['All ATs', ...atNames].map(key => {
- let count;
- if (key === 'All ATs') {
- count = testPlanVersionTests.length;
- } else {
- count = testPlanVersionTests.filter(test =>
- test.ats.find(at => at.name === key)
- ).length;
- }
- return [key, `${key} (${count})`];
- })
+ let filteredTests;
+ if (activeFilter === 'All ATs') {
+ filteredTests = testPlanVersionTests;
+ } else {
+ filteredTests = testPlanVersionTests.filter(test =>
+ test.ats.find(at => at.name === activeFilter)
);
+ }
- return (
-
-
- {`${testPlanVersion.title} Test Plan ${testPlanVersion.versionString} | ARIA-AT`}
-
-
- {`${testPlanVersion.title} Test Plan ` +
- `${testPlanVersion.versionString}` +
- `${testPlanVersion.deprecatedAt ? ' (Deprecated)' : ''}`}
-
- About This Test Plan
-
-
- Phase:
- {derivePhaseName(testPlanVersion.phase)}
-
-
- Version:
- {`${testPlanVersion.versionString} `}
-
- (View all versions of this test plan)
-
-
-
- Version History:
-
-
- {`R&D completed on ${convertDateToString(
- testPlanVersion.updatedAt,
- 'MMM D, YYYY'
- )}.`}
-
- {!testPlanVersion.draftPhaseReachedAt ? null : (
-
- {`ARIA-AT draft review process started on ` +
- `${convertDateToString(
- testPlanVersion.draftPhaseReachedAt,
- 'MMM D, YYYY'
- )} ` +
- `for this version.`}
-
- )}
- {!testPlanVersion.candidatePhaseReachedAt ? null : (
-
- {`ARIA-AT candidate review process started on ` +
- `${convertDateToString(
- testPlanVersion.candidatePhaseReachedAt,
- 'MMM D, YYYY'
- )} ` +
- `for this version.`}
-
- )}
- {!testPlanVersion.recommendedPhaseReachedAt ? null : (
-
- {`Version reached ARIA-AT recommended status on ` +
- `${convertDateToString(
- testPlanVersion.recommendedPhaseReachedAt,
- 'MMM D, YYYY'
- )}.`}
-
- )}
- {!testPlanVersion.deprecatedAt ? null : (
-
- {`Version deprecated on ` +
- `${convertDateToString(
- testPlanVersion.deprecatedAt,
- 'MMM D, YYYY'
- )}.`}
-
- )}
-
+ const filterOptions = Object.fromEntries(
+ ['All ATs', ...atNames].map(key => {
+ let count;
+ if (key === 'All ATs') {
+ count = testPlanVersionTests.length;
+ } else {
+ count = testPlanVersionTests.filter(test =>
+ test.ats.find(at => at.name === key)
+ ).length;
+ }
+ return [key, `${key} (${count})`];
+ })
+ );
+
+ return (
+
+
+ {`${testPlanVersion.title} Test Plan ${testPlanVersion.versionString} | ARIA-AT`}
+
+
+ {`${testPlanVersion.title} Test Plan ` +
+ `${testPlanVersion.versionString}` +
+ `${testPlanVersion.deprecatedAt ? ' (Deprecated)' : ''}`}
+
+ About This Test Plan
+
+
+ Phase:
+ {derivePhaseName(testPlanVersion.phase)}
+
+
+ Version:
+ {`${testPlanVersion.versionString} `}
+
+ (View all versions of this test plan)
+
+
+
+ Version History:
+
+
+ {`R&D completed on ${convertDateToString(
+ testPlanVersion.updatedAt,
+ 'MMM D, YYYY'
+ )}.`}
+
+ {!testPlanVersion.draftPhaseReachedAt ? null : (
+
+ {`ARIA-AT draft review process started on ` +
+ `${convertDateToString(
+ testPlanVersion.draftPhaseReachedAt,
+ 'MMM D, YYYY'
+ )} ` +
+ `for this version.`}
+
+ )}
+ {!testPlanVersion.candidatePhaseReachedAt ? null : (
+
+ {`ARIA-AT candidate review process started on ` +
+ `${convertDateToString(
+ testPlanVersion.candidatePhaseReachedAt,
+ 'MMM D, YYYY'
+ )} ` +
+ `for this version.`}
+
+ )}
+ {!testPlanVersion.recommendedPhaseReachedAt ? null : (
+
+ {`Version reached ARIA-AT recommended status on ` +
+ `${convertDateToString(
+ testPlanVersion.recommendedPhaseReachedAt,
+ 'MMM D, YYYY'
+ )}.`}
+
+ )}
+ {!testPlanVersion.deprecatedAt ? null : (
+
+ {`Version deprecated on ` +
+ `${convertDateToString(
+ testPlanVersion.deprecatedAt,
+ 'MMM D, YYYY'
+ )}.`}
+
+ )}
+
+
+
+ Latest Commit:
+
+ {testPlanVersion.gitMessage}
+
+
+
+
+ Supporting Documentation
+
+ {testPlanVersionTests[0].renderableContents[0].renderableContent.info.references.map(
+ reference => {
+ if (isV2) {
+ let refValue = '';
+ let refLinkText = '';
+ if (
+ reference.refId === 'example' ||
+ reference.refId === 'designPattern' ||
+ reference.refId === 'developmentDocumentation'
+ ) {
+ refValue = reference.value;
+ refLinkText = reference.linkText;
+ }
+ return refValue ? (
+
+
+ {refLinkText}
+
-
- Latest Commit:
-
- {testPlanVersion.gitMessage}
-
+ ) : null;
+ } else {
+ return (
+
+
+ {reference.refId}
+
-
+ );
+ }
+ }
+ )}
+
+ Tests
+
+ {
+ setActiveFilter(selectedFilter);
+ }}
+ />
+
+ {filteredTests.map((test, index) => {
+ const isFirst = index === 0;
+ const hasAriaReference =
+ test.renderableContents[0].renderableContent.info.references.some(
+ reference => reference.type === 'aria'
+ );
+
+ let filteredAts;
+ if (activeFilter === 'All ATs') {
+ filteredAts = test.ats;
+ } else {
+ filteredAts = test.ats.filter(at => at.name === activeFilter);
+ }
- Supporting Documentation
-
+ >
+ ) : null}
+ {filteredAts.map(at => {
+ const renderableContent = test.renderableContents.find(
+ ({ at: renderableContentAt }) =>
+ at.id === renderableContentAt.id
+ ).renderableContent;
- return (
-
- {at.name}
- Instructions
-
-
- );
- })}
-
- );
+ return (
+
+ {at.name}
+ Instructions
+
+
+ );
})}
-
- );
+
+ );
+ })}
+
+ );
};
export default TestReview;
diff --git a/client/components/TestReview/queries.js b/client/components/TestReview/queries.js
index fe3370c93..4a0b152d3 100644
--- a/client/components/TestReview/queries.js
+++ b/client/components/TestReview/queries.js
@@ -1,45 +1,45 @@
import { gql } from '@apollo/client';
export const TEST_REVIEW_PAGE_QUERY = gql`
- query TestReviewPageQuery($testPlanVersionId: ID!) {
- testPlanVersion(id: $testPlanVersionId) {
+ query TestReviewPageQuery($testPlanVersionId: ID!) {
+ testPlanVersion(id: $testPlanVersionId) {
+ id
+ title
+ versionString
+ gitSha
+ gitMessage
+ testPageUrl
+ phase
+ updatedAt
+ draftPhaseReachedAt
+ candidatePhaseReachedAt
+ recommendedPhaseReachedAt
+ deprecatedAt
+ testPlan {
+ directory
+ }
+ tests {
+ id
+ rowNumber
+ title
+ ats {
+ id
+ name
+ }
+ renderedUrls {
+ at {
+ id
+ }
+ renderedUrl
+ }
+ renderableContents {
+ at {
id
- title
- versionString
- gitSha
- gitMessage
- testPageUrl
- phase
- updatedAt
- draftPhaseReachedAt
- candidatePhaseReachedAt
- recommendedPhaseReachedAt
- deprecatedAt
- testPlan {
- directory
- }
- tests {
- id
- rowNumber
- title
- ats {
- id
- name
- }
- renderedUrls {
- at {
- id
- }
- renderedUrl
- }
- renderableContents {
- at {
- id
- }
- renderableContent
- }
- }
- metadata
+ }
+ renderableContent
}
+ }
+ metadata
}
+ }
`;
diff --git a/client/components/TestRun/CollectionJobContext.js b/client/components/TestRun/CollectionJobContext.js
new file mode 100644
index 000000000..af16f4f83
--- /dev/null
+++ b/client/components/TestRun/CollectionJobContext.js
@@ -0,0 +1,71 @@
+import PropTypes from 'prop-types';
+import React, { createContext, useState, useEffect } from 'react';
+import { useLazyQuery } from '@apollo/client';
+import { COLLECTION_JOB_UPDATES_QUERY } from './queries';
+import { isJobStatusFinal } from '../../utils/collectionJobStatus';
+const pollInterval = 5000;
+
+export const Context = createContext({
+ state: {
+ collectionJob: null
+ },
+ actions: {}
+});
+
+export const Provider = ({ children, testPlanRun }) => {
+ if (!testPlanRun) {
+ // Anonymous page / not working, just viewing the tests, no need for the
+ // provider to provide any data or updates, but to be consistent we will
+ // still wrap with a provider with static data
+ return (
+
+ {children}
+
+ );
+ }
+ const { id: testPlanRunId, collectionJob: initialCollectionJob } =
+ testPlanRun;
+ const [providerValue, setProviderValue] = useState({
+ state: { collectionJob: initialCollectionJob },
+ actions: {}
+ });
+
+ const [, { data: collectionJobUpdateData, startPolling, stopPolling }] =
+ testPlanRunId
+ ? useLazyQuery(COLLECTION_JOB_UPDATES_QUERY, {
+ fetchPolicy: 'cache-and-network',
+ variables: { collectionJobId: initialCollectionJob?.id },
+ pollInterval
+ })
+ : {};
+
+ // control the data flow, turn on polling if this is a collection job report
+ // that still has possible updates.
+ useEffect(() => {
+ // use the colllection job from the polling update first priority
+ // otherwise, default to the first data fetch from the API
+ const collectionJob =
+ collectionJobUpdateData?.collectionJob ?? initialCollectionJob;
+ const status = collectionJob?.status;
+ if (collectionJob && !isJobStatusFinal(status)) {
+ startPolling(pollInterval);
+ } else {
+ stopPolling();
+ }
+ setProviderValue({ state: { collectionJob }, actions: {} });
+ }, [collectionJobUpdateData]);
+
+ return {children} ;
+};
+
+Provider.propTypes = {
+ children: PropTypes.node,
+ testPlanRun: PropTypes.shape({
+ id: PropTypes.string,
+ collectionJob: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ status: PropTypes.string.isRequired,
+ testStatus: PropTypes.arrayOf(PropTypes.object).isRequired
+ })
+ })
+};
diff --git a/client/components/TestRun/Heading.js b/client/components/TestRun/Heading.js
new file mode 100644
index 000000000..cd559be81
--- /dev/null
+++ b/client/components/TestRun/Heading.js
@@ -0,0 +1,172 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+import { Button } from 'react-bootstrap';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import {
+ faEdit,
+ faCheck,
+ faExclamationCircle,
+ faRobot
+} from '@fortawesome/free-solid-svg-icons';
+import { Context } from './CollectionJobContext';
+import {
+ COLLECTION_JOB_STATUS,
+ isJobStatusFinal
+} from '../../utils/collectionJobStatus';
+
+const TestRunHeading = ({
+ at,
+ browser,
+ editAtBrowserDetailsButtonRef,
+ handleEditAtBrowserDetailsClick,
+ isSignedIn,
+ openAsUser,
+ showEditAtBrowser,
+ testPlanTitle,
+ testResults,
+ testCount
+}) => {
+ const {
+ state: { collectionJob }
+ } = useContext(Context);
+
+ const renderTestsCompletedInfoBox = () => {
+ let isReviewingBot = Boolean(openAsUser?.isBot);
+ let content;
+
+ if (isReviewingBot) {
+ const countTestResults = testResults.reduce(
+ (acc, { scenarioResults }) =>
+ acc +
+ (scenarioResults && scenarioResults.every(({ output }) => !!output)
+ ? 1
+ : 0),
+ 0
+ );
+ const countCompleteCollection = collectionJob.testStatus.reduce(
+ (acc, { status }) =>
+ acc + (status === COLLECTION_JOB_STATUS.COMPLETED ? 1 : 0),
+ 0
+ );
+
+ content = (
+ <>
+
+ {`${Math.max(
+ countTestResults,
+ countCompleteCollection
+ )} of ${testCount}`} {' '}
+ responses collected.
+
+
+ Collection Job Status: {collectionJob.status}
+
+ >
+ );
+ } else if (!isSignedIn) {
+ content = {testCount} tests to view ;
+ } else if (testCount) {
+ content = (
+ <>
+ {' '}
+ {`${testResults.reduce(
+ (acc, { completedAt }) => acc + (completedAt ? 1 : 0),
+ 0
+ )} of ${testCount}`} {' '}
+ tests completed
+ >
+ );
+ } else {
+ content = No tests for this AT and Browser combination
;
+ }
+
+ return (
+
+ );
+ };
+
+ let openAsUserHeading = null;
+
+ if (openAsUser?.isBot) {
+ openAsUserHeading = (
+
+ Reviewing tests of {' '}
+ {`${openAsUser.username}`}.
+ {!isJobStatusFinal(collectionJob.status) && (
+ <>
+
+ The collection bot is still updating information on this page.
+ Changes may be lost when updates arrive.
+ >
+ )}
+
+ );
+ } else if (openAsUser) {
+ openAsUserHeading = (
+
+ Reviewing tests of
{`${openAsUser.username}`}.
+
{`All changes will be saved as performed by ${openAsUser.username}.`}
+
+ );
+ }
+
+ return (
+ <>
+
+
+
+ Test Plan: {testPlanTitle}
+
+
+
+
+
+ AT: {at}
+
+
+ Browser: {browser}
+
+
+ {showEditAtBrowser && (
+
+
+
+ )}
+
+ {renderTestsCompletedInfoBox()}
+
+ {openAsUserHeading}
+ >
+ );
+};
+
+TestRunHeading.propTypes = {
+ testPlanTitle: PropTypes.string.isRequired,
+ at: PropTypes.string.isRequired,
+ browser: PropTypes.string.isRequired,
+ showEditAtBrowser: PropTypes.bool.isRequired,
+ editAtBrowserDetailsButtonRef: PropTypes.object.isRequired,
+ isSignedIn: PropTypes.bool.isRequired,
+ openAsUser: PropTypes.shape({
+ isBot: PropTypes.bool.isRequired,
+ username: PropTypes.string.isRequired
+ }),
+ testResults: PropTypes.arrayOf(PropTypes.shape({})),
+ testCount: PropTypes.number.isRequired,
+ handleEditAtBrowserDetailsClick: PropTypes.func.isRequired
+};
+
+export default TestRunHeading;
diff --git a/client/components/TestRun/OptionButton.js b/client/components/TestRun/OptionButton.js
index 8ca4eccf8..3cee6a910 100644
--- a/client/components/TestRun/OptionButton.js
+++ b/client/components/TestRun/OptionButton.js
@@ -3,34 +3,34 @@ import PropTypes from 'prop-types';
import { Button } from 'react-bootstrap';
const OptionButton = ({
- text,
- icon = null,
- disabled = false,
- onClick,
- children,
- ...restProps
+ text,
+ icon = null,
+ disabled = false,
+ onClick,
+ children,
+ ...restProps
}) => {
- return (
-
- {icon}
- {text || children}
-
- );
+ return (
+
+ {icon}
+ {text || children}
+
+ );
};
OptionButton.propTypes = {
- text: PropTypes.string,
- icon: PropTypes.node,
- disabled: PropTypes.bool,
- onClick: PropTypes.func,
- children: PropTypes.node
+ text: PropTypes.string,
+ icon: PropTypes.node,
+ disabled: PropTypes.bool,
+ onClick: PropTypes.func,
+ children: PropTypes.node
};
export default OptionButton;
diff --git a/client/components/TestRun/ReviewConflictsModal/index.jsx b/client/components/TestRun/ReviewConflictsModal/index.jsx
index 03cb5e97a..94dcf5b03 100644
--- a/client/components/TestRun/ReviewConflictsModal/index.jsx
+++ b/client/components/TestRun/ReviewConflictsModal/index.jsx
@@ -5,66 +5,64 @@ import styled from '@emotion/styled';
import ReviewConflicts from '../../ReviewConflicts';
const H2 = styled.h2`
- margin-top: 0;
+ margin-top: 0;
`;
const ReviewConflictsModal = ({
- testPlanReport,
- test,
- issueLink,
- conflictMarkdown,
- show = false,
- handleClose = () => {}
+ testPlanReport,
+ test,
+ issueLink,
+ conflictMarkdown,
+ show = false,
+ handleClose = () => {}
}) => {
- return (
-
+
+
+ Review Conflicts for "{test.title}"
+
+
+
+
+
+
+ navigator.clipboard.writeText(conflictMarkdown)}
>
-
-
- Review Conflicts for "{test.title}"
-
-
-
-
-
-
-
- navigator.clipboard.writeText(conflictMarkdown)
- }
- >
- Copy Conflicts to Clipboard
-
-
- Raise an Issue for Conflict
-
-
-
- );
+ Copy Conflicts to Clipboard
+
+
+ Raise an Issue for Conflict
+
+
+
+ );
};
ReviewConflictsModal.propTypes = {
- show: PropTypes.bool,
- testPlanVersion: PropTypes.object.isRequired,
- testPlanReport: PropTypes.object.isRequired,
- test: PropTypes.object.isRequired,
- handleClose: PropTypes.func,
- conflictMarkdown: PropTypes.string,
- issueLink: PropTypes.string.isRequired
+ show: PropTypes.bool,
+ testPlanVersion: PropTypes.object.isRequired,
+ testPlanReport: PropTypes.object.isRequired,
+ test: PropTypes.object.isRequired,
+ handleClose: PropTypes.func,
+ conflictMarkdown: PropTypes.string,
+ issueLink: PropTypes.string.isRequired
};
export default ReviewConflictsModal;
diff --git a/client/components/TestRun/StatusBar/index.jsx b/client/components/TestRun/StatusBar/index.jsx
index f034caf6b..442ca0d62 100644
--- a/client/components/TestRun/StatusBar/index.jsx
+++ b/client/components/TestRun/StatusBar/index.jsx
@@ -5,61 +5,56 @@ import { Octicon, Octicons } from 'octicons-react';
import nextId from 'react-id-generator';
const StatusBar = ({
- hasConflicts = false,
- handleReviewConflictsButtonClick = () => {}
+ hasConflicts = false,
+ handleReviewConflictsButtonClick = () => {}
}) => {
- const [statuses, setStatuses] = useState([]);
+ const [statuses, setStatuses] = useState([]);
- useEffect(() => {
- const statuses = [];
+ useEffect(() => {
+ const statuses = [];
- if (hasConflicts) {
- const variant = 'warning';
- const action = (
-
- Review Conflicts
-
- );
- const icon = 'alert';
- const message = 'This test has conflicting results';
- statuses.push({
- action,
- icon,
- message,
- variant
- });
- }
+ if (hasConflicts) {
+ const variant = 'warning';
+ const action = (
+
+ Review Conflicts
+
+ );
+ const icon = 'alert';
+ const message = 'This test has conflicting results';
+ statuses.push({
+ action,
+ icon,
+ message,
+ variant
+ });
+ }
- setStatuses(statuses);
- }, []);
+ setStatuses(statuses);
+ }, []);
- return (
- <>
- {statuses.map(({ action, icon, message, variant }) => {
- return (
-
- {' '}
- {message}
- {action}
-
- );
- })}
- >
- );
+ return (
+ <>
+ {statuses.map(({ action, icon, message, variant }) => {
+ return (
+
+ {message}
+ {action}
+
+ );
+ })}
+ >
+ );
};
StatusBar.propTypes = {
- issues: PropTypes.array,
- hasConflicts: PropTypes.bool,
- handleReviewConflictsButtonClick: PropTypes.func
+ issues: PropTypes.array,
+ hasConflicts: PropTypes.bool,
+ handleReviewConflictsButtonClick: PropTypes.func
};
export default StatusBar;
diff --git a/client/components/TestRun/TestIframe/index.jsx b/client/components/TestRun/TestIframe/index.jsx
deleted file mode 100644
index a560e6df8..000000000
--- a/client/components/TestRun/TestIframe/index.jsx
+++ /dev/null
@@ -1,174 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { serialize, hydrate } from '../../../utils/formSerialization';
-
-class TestIframe extends Component {
- constructor(props) {
- super(props);
-
- this.handleResultsMessage = this.handleResultsMessage.bind(this);
-
- this.iframeEl = React.createRef();
- }
-
- // validate that postMessage comes from the same origin,
- // validate that message has expected shape,
- // and test if message type matches given type
- validateMessage(message, type) {
- if (window.location.origin !== message.origin) {
- return false;
- }
- if (!message.data || typeof message.data !== 'object') {
- return false;
- }
- if (message.data.type !== type) {
- return false;
- }
- return true;
- }
-
- handleResultsMessage(message) {
- if (!this.validateMessage(message, 'results')) return;
- const { data } = message.data;
- this.processResults(data);
- }
-
- async processResults(results) {
- // stop listening for additional results if we are saving results
- window.removeEventListener('message', this.handleResultsMessage);
-
- // capture serialized form state from the iframe
- const serializedForm = this.serializeForm();
-
- const { saveTestResultOrProgress } = this.props;
- await saveTestResultOrProgress({
- results,
- serializedForm
- });
- }
-
- serializeForm() {
- const documentEl = this.iframeEl.current.contentDocument;
- const resultsEl = documentEl.querySelector('#record-results');
- return serialize(resultsEl);
- }
-
- // if aria-at test harness has not loaded
- // we have to attach a one-time event handler
- // to listen for the custom 'loaded' postMessage
- // message since DOMContentLoaded is called
- // before the harness dynamically sets up the
- // test html
- async waitForTestHarnessReload() {
- return new Promise(resolve => {
- const handleLoadMessage = message => {
- if (!this.validateMessage(message, 'loaded')) return;
- window.removeEventListener('message', handleLoadMessage);
- resolve();
- };
- window.addEventListener('message', handleLoadMessage);
- // trigger reload
- this.iframeEl.current.src = this.iframeEl.current.src; // eslint-disable-line no-self-assign
- });
- }
-
- async reloadAndHydrate(serialized) {
- // perform the hydration from serialized form state
- const documentEl = this.iframeEl.current.contentDocument;
- const resultsEl = documentEl.querySelector('#record-results');
- hydrate(serialized, resultsEl);
- }
-
- /* Public function called by test runner when existing incomplete test */
- async saveTestProgress() {
- // capture serialized form state from the iframe
- const serializedForm = this.serializeForm();
- let saved = false;
-
- // Only save if there are partial results to save
- if (JSON.stringify(serializedForm) !== this.emptyForm) {
- const { saveTestResultOrProgress } = this.props;
- await saveTestResultOrProgress({
- serializedForm
- });
- saved = true;
- }
- return saved;
- }
-
- /* Public function called by test runner when intending to save complete result */
- triggerSubmit() {
- this.iframeEl.current.contentWindow.postMessage(
- {
- type: 'submit'
- },
- window.location.origin
- );
- }
-
- /* Public function for deleting partial or complete results */
- async reloadAndClear() {
- await this.waitForTestHarnessReload();
- }
-
- async componentDidMount() {
- // listen for 'results' postMessage from child iframe
- // which is sent when the "Review Results" button is clicked
- // inside the iframe
- window.addEventListener('message', this.handleResultsMessage);
-
- // Before hydrating, save local copy of serialized state with no results
- // This is a short cut to tell whether the form has been edited
- await this.waitForTestHarnessReload();
- this.emptyForm = JSON.stringify(this.serializeForm());
-
- // Load partial results
- const { serializedForm } = this.props;
- if (serializedForm) {
- this.reloadAndHydrate(serializedForm);
- }
- }
-
- componentWillUnmount() {
- // stop listening for results if we haven't already above
- window.removeEventListener('message', this.handleResultsMessage);
- }
-
- async componentDidUpdate(prevProps) {
- // If the test we are looking at changes
- if (prevProps.file !== this.props.file) {
- // Before hydrating, save local copy of serialized state with no results
- // This is a short cut to tell whether the form has been edited
- await this.waitForTestHarnessReload();
- this.emptyForm = JSON.stringify(this.serializeForm());
-
- // Load partial results
- const { serializedForm } = this.props;
- if (serializedForm) {
- this.reloadAndHydrate(serializedForm);
- }
- }
- }
-
- render() {
- const { git_hash, file, at_key } = this.props;
-
- return (
-
- );
- }
-}
-
-export default TestIframe;
-
-TestIframe.propTypes = {
- saveTestResultOrProgress: PropTypes.func,
- git_hash: PropTypes.string,
- file: PropTypes.string,
- at_key: PropTypes.string,
- serializedForm: PropTypes.array
-};
diff --git a/client/components/TestRun/TestNavigator.jsx b/client/components/TestRun/TestNavigator.jsx
index e1aba2b0c..0fe1fd43c 100644
--- a/client/components/TestRun/TestNavigator.jsx
+++ b/client/components/TestRun/TestNavigator.jsx
@@ -1,171 +1,164 @@
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
- faAlignLeft,
- faArrowLeft,
- faArrowRight
+ faAlignLeft,
+ faArrowLeft,
+ faArrowRight
} from '@fortawesome/free-solid-svg-icons';
import { Col } from 'react-bootstrap';
-import React, { useMemo } from 'react';
+import React, { useContext, useMemo } from 'react';
+import { Context as CollectionJobContext } from './CollectionJobContext';
import '@fortawesome/fontawesome-svg-core/styles.css';
-import { COLLECTION_JOB_STATUS_BY_TEST_PLAN_RUN_ID_QUERY } from './queries';
-import { useQuery } from '@apollo/client';
const TestNavigator = ({
- show = true,
- isSignedIn = false,
- viewedTests = [],
- isVendor = false,
- testPlanReport = {},
- tests = [],
- currentTestIndex = 0,
- toggleShowClick = () => {},
- handleTestClick = () => {},
- testPlanRun = null
+ show = true,
+ isSignedIn = false,
+ viewedTests = [],
+ isVendor = false,
+ testPlanReport = {},
+ tests = [],
+ currentTestIndex = 0,
+ toggleShowClick = () => {},
+ handleTestClick = () => {},
+ testPlanRun = null
}) => {
- const isBotCompletedTest = testPlanRun?.tester?.isBot;
+ const isBotCompletedTest = testPlanRun?.tester?.isBot;
- const { data: collectionJobQuery } = useQuery(
- COLLECTION_JOB_STATUS_BY_TEST_PLAN_RUN_ID_QUERY,
- {
- variables: {
- testPlanRunId: testPlanRun?.id
- }
- }
- );
-
- const status = useMemo(() => {
- return collectionJobQuery?.collectionJobByTestPlanRunId?.status;
- }, [collectionJobQuery]);
+ const {
+ state: { collectionJob }
+ } = useContext(CollectionJobContext);
+ const testStatus = useMemo(
+ () => collectionJob?.testStatus ?? [],
+ [collectionJob]
+ );
- return (
-
-
-
-
- {show ? (
-
- ) : (
-
- )}
-
-
-
-
-
-
- {tests.map(test => {
- let resultClassName = 'not-started';
- let resultStatus = 'Not Started';
+ return (
+
+
+
+
+ {show ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ {tests.map(test => {
+ let resultClassName = 'not-started';
+ let resultStatus = 'Not Started';
- const issuesExist = testPlanReport.issues?.filter(
- issue =>
- issue.isCandidateReview &&
- issue.testNumberFilteredByAt == test.seq
- ).length;
+ const issuesExist = testPlanReport.issues?.filter(
+ issue =>
+ issue.isCandidateReview &&
+ issue.testNumberFilteredByAt == test.seq
+ ).length;
- if (test) {
- if (isBotCompletedTest) {
- if (
- test.testResult?.scenarioResults.some(
- s => s.output
- )
- ) {
- resultClassName = 'bot-complete';
- resultStatus = 'Completed by Bot';
- } else if (status !== 'CANCELLED') {
- resultClassName = 'bot-queued';
- resultStatus = 'In Progress by Bot';
- } else {
- resultClassName = 'bot-cancelled';
- resultStatus = 'Cancelled by Bot';
- }
- } else {
- // Non-bot tests
- if (test.hasConflicts) {
- resultClassName = 'conflicts';
- resultStatus = 'Has Conflicts';
- } else if (test.testResult) {
- resultClassName = test.testResult
- .completedAt
- ? 'complete'
- : 'in-progress';
- resultStatus = test.testResult.completedAt
- ? 'Complete Test'
- : 'In Progress';
- } else if (
- !isSignedIn &&
- !isVendor &&
- test.index === currentTestIndex
- ) {
- resultClassName = 'in-progress';
- resultStatus = 'In Progress:';
- } else if (isVendor) {
- if (issuesExist) {
- resultClassName = 'changes-requested';
- resultStatus = 'Changes Requested';
- } else if (viewedTests.includes(test.id)) {
- resultClassName = 'complete';
- resultStatus = 'Test Viewed';
- }
- }
- }
- }
- return (
-
-
- await handleTestClick(test.index)
- }
- className="test-name"
- aria-current={
- test.index === currentTestIndex
- }
- >
- {test.title}
-
-
-
- );
- })}
-
-
-
- );
+ if (test) {
+ if (isBotCompletedTest) {
+ const { status } =
+ testStatus.find(ts => ts.test.id === test.id) ?? {};
+ if (status === 'COMPLETED') {
+ resultClassName = 'bot-complete';
+ resultStatus = 'Completed by Bot';
+ } else if (status === 'QUEUED') {
+ resultClassName = 'bot-queued';
+ resultStatus = 'Queued by Bot';
+ } else if (status === 'RUNNING') {
+ resultClassName = 'bot-running';
+ resultStatus = 'Running with Bot';
+ } else if (status === 'ERROR') {
+ resultClassName = 'bot-error';
+ resultStatus = 'Error collecting with Bot';
+ } else if (status === 'CANCELLED') {
+ resultClassName = 'bot-cancelled';
+ resultStatus = 'Cancelled by Bot';
+ }
+ } else {
+ // Non-bot tests
+ if (test.hasConflicts) {
+ resultClassName = 'conflicts';
+ resultStatus = 'Has Conflicts';
+ } else if (test.testResult) {
+ resultClassName = test.testResult.completedAt
+ ? 'complete'
+ : 'in-progress';
+ resultStatus = test.testResult.completedAt
+ ? 'Complete Test'
+ : 'In Progress';
+ } else if (
+ !isSignedIn &&
+ !isVendor &&
+ test.index === currentTestIndex
+ ) {
+ resultClassName = 'in-progress';
+ resultStatus = 'In Progress:';
+ } else if (isVendor) {
+ if (issuesExist) {
+ resultClassName = 'changes-requested';
+ resultStatus = 'Changes Requested';
+ } else if (viewedTests.includes(test.id)) {
+ resultClassName = 'complete';
+ resultStatus = 'Test Viewed';
+ }
+ }
+ }
+ }
+ return (
+
+ await handleTestClick(test.index)}
+ className="test-name"
+ aria-current={test.index === currentTestIndex}
+ >
+ {test.title}
+
+
+
+ );
+ })}
+
+
+
+ );
};
TestNavigator.propTypes = {
- show: PropTypes.bool,
- isSignedIn: PropTypes.bool,
- isVendor: PropTypes.bool,
- testPlanReport: PropTypes.object,
- tests: PropTypes.array,
- testResult: PropTypes.object,
- conflicts: PropTypes.object,
- currentTestIndex: PropTypes.number,
- viewedTests: PropTypes.array,
- toggleShowClick: PropTypes.func,
- handleTestClick: PropTypes.func,
- testPlanRun: PropTypes.object
+ show: PropTypes.bool,
+ isSignedIn: PropTypes.bool,
+ isVendor: PropTypes.bool,
+ testPlanReport: PropTypes.object,
+ tests: PropTypes.array,
+ testResult: PropTypes.object,
+ conflicts: PropTypes.object,
+ currentTestIndex: PropTypes.number,
+ viewedTests: PropTypes.array,
+ toggleShowClick: PropTypes.func,
+ handleTestClick: PropTypes.func,
+ testPlanRun: PropTypes.object
};
export default TestNavigator;
diff --git a/client/components/TestRun/TestRun.css b/client/components/TestRun/TestRun.css
index 25cc521fd..d69d6bfb7 100644
--- a/client/components/TestRun/TestRun.css
+++ b/client/components/TestRun/TestRun.css
@@ -1,322 +1,343 @@
@import url('https://use.fontawesome.com/releases/v5.0.1/css/all.css');
.main-test-area {
- position: relative;
+ position: relative;
}
/* Test Navigator */
.test-navigator h2 {
- margin-top: 0;
+ margin-top: 0;
}
#test-navigator-heading {
- font-size: 1em;
- margin-top: 0.75em;
+ font-size: 1em;
+ margin-top: 0.75em;
}
button.test-navigator-toggle {
- border: 0;
- background-color: transparent;
- color: #929292;
+ border: 0;
+ background-color: transparent;
+ color: #929292;
}
button.test-navigator-toggle:hover,
button.test-navigator-toggle:focus {
- color: #0b60ab;
+ color: #0b60ab;
}
.test-navigator-toggle-container {
- display: flex;
- flex-direction: row;
+ display: flex;
+ flex-direction: row;
}
.test-navigator-toggle-inner-container {
- width: fit-content;
+ width: fit-content;
}
@media all and (max-width: 767px) {
- .test-navigator-toggle-inner-container {
- position: relative;
- top: -0.15em;
- }
+ .test-navigator-toggle-inner-container {
+ position: relative;
+ top: -0.15em;
+ }
}
@media all and (min-width: 768px) {
- .test-navigator-toggle-inner-container {
- position: absolute;
- top: -0.15em;
- left: -2.75em;
- }
+ .test-navigator-toggle-inner-container {
+ position: absolute;
+ top: -0.15em;
+ left: -2.75em;
+ }
}
.test-navigator-list {
- position: relative;
- margin-top: 1em;
- font-size: 0.9em;
- padding-left: 3.25em;
+ position: relative;
+ margin-top: 1em;
+ font-size: 0.9em;
+ padding-left: 3.25em;
}
.test-name-wrapper {
- position: relative;
- margin: 0 0 1em 0.25em;
- list-style: unset;
+ position: relative;
+ margin: 0 0 1em 0.25em;
+ list-style: unset;
}
.test-name-wrapper:before {
- content: '';
- height: 100%;
- left: -2.8em;
- position: absolute;
- top: 1.2em;
- background: #d2d5d9;
- width: 2px;
+ content: '';
+ height: 100%;
+ left: -2.8em;
+ position: absolute;
+ top: 1.2em;
+ background: #d2d5d9;
+ width: 2px;
}
.test-name-wrapper:last-child:before {
- height: 0%;
+ height: 0%;
}
.complete.test-name-wrapper:before,
.changes-requested.test-name-wrapper:before {
- background: #1d8f37;
+ background: #1d8f37;
}
.progress-indicator {
- position: absolute;
- left: -3.35em;
- top: 0.2em;
- width: 18px;
- height: 18px;
- border-radius: 50px;
+ position: absolute;
+ left: -3.35em;
+ top: 0.2em;
+ width: 18px;
+ height: 18px;
+ border-radius: 50px;
}
.test-name-wrapper .test-name {
- display: block;
- color: #2f2f2f;
+ display: block;
+ color: #2f2f2f;
}
.test-name-wrapper .test-name:hover {
- color: #0b60ab;
+ color: #0b60ab;
}
.test-navigator a[aria-current='true'] ~ span {
- box-shadow: 0px 0px 0px 2px #ffffff, 0px 0px 0px 4px #1d8f37;
+ box-shadow: 0px 0px 0px 2px #ffffff, 0px 0px 0px 4px #1d8f37;
}
/* Test States in Test Navigator */
.test-name-wrapper.not-started .progress-indicator {
- background: #d2d5d9;
+ background: #d2d5d9;
}
.test-name-wrapper.in-progress .progress-indicator {
- background: #1e8f37;
- background: linear-gradient(
- 135deg,
- #1e8f37 0%,
- #1e8f37 50%,
- rgba(255, 255, 255, 1) 50%,
- rgba(255, 255, 255, 1) 100%
- );
- border: 2px solid #1e8f37;
+ background: #1e8f37;
+ background: linear-gradient(
+ 135deg,
+ #1e8f37 0%,
+ #1e8f37 50%,
+ rgba(255, 255, 255, 1) 50%,
+ rgba(255, 255, 255, 1) 100%
+ );
+ border: 2px solid #1e8f37;
}
.test-name-wrapper.changes-requested .progress-indicator {
- background: #f87f1c;
+ background: #f87f1c;
}
.test-name-wrapper.changes-requested .progress-indicator:before {
- position: relative;
- content: '\f024';
- font-family: 'Font Awesome 5 Free';
- font-weight: 900;
- color: white;
- font-size: 10px;
- top: -3px;
- left: 4px;
+ position: relative;
+ content: '\f024';
+ font-family: 'Font Awesome 5 Free';
+ font-weight: 900;
+ color: white;
+ font-size: 10px;
+ top: -3px;
+ left: 4px;
}
.test-name-wrapper.skipped .progress-indicator {
- background: White;
- border: 2px dashed black;
+ background: White;
+ border: 2px dashed black;
}
.test-name-wrapper.bot-complete .progress-indicator {
- background: #1e8f37;
+ background: #1e8f37;
}
.test-name-wrapper.bot-complete .progress-indicator:before {
- display: inline-block;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- font-family: 'Font Awesome 5 Free';
- font-weight: 900;
- content: '\f00c';
- color: white;
- font-size: 10px;
- position: relative;
- top: -3px;
- left: 4px;
+ display: inline-block;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ font-family: 'Font Awesome 5 Free';
+ font-weight: 900;
+ content: '\f00c';
+ color: white;
+ font-size: 10px;
+ position: relative;
+ top: -3px;
+ left: 4px;
+}
+
+.test-name-wrapper.bot-running .progress-indicator {
+ background: #d2d5d9;
+ border: 2px solid #1e8f37;
+}
+
+.test-name-wrapper.bot-error .progress-indicator {
+ background: #e3261f;
+}
+.test-name-wrapper.bot-error .progress-indicator:before {
+ display: inline-block;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ font-family: 'Font Awesome 5 Free';
+ font-weight: 900;
+ content: '\f071';
+ color: white;
+ font-size: 10px;
+ position: relative;
+ top: -4px;
+ left: 3px;
}
-
.test-name-wrapper.bot-queued .progress-indicator {
- background: #295fa6;
+ background: #295fa6;
}
.test-name-wrapper.bot-queued .progress-indicator:before {
- display: inline-block;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- font-family: 'Font Awesome 5 Free';
- font-weight: 900;
- content: '\f061';
- color: white;
- font-size: 10px;
- position: relative;
- top: -3px;
- left: 4px;
+ display: inline-block;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ font-family: 'Font Awesome 5 Free';
+ font-weight: 900;
+ content: '\f061';
+ color: white;
+ font-size: 10px;
+ position: relative;
+ top: -3px;
+ left: 4px;
}
.test-name-wrapper.bot-cancelled .progress-indicator {
- background: #a331fe;
+ background: #a331fe;
}
.test-name-wrapper.bot-cancelled .progress-indicator:before {
- display: inline-block;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- font-family: 'Font Awesome 5 Free';
- font-weight: 900;
- content: '\f05e';
- color: white;
- font-size: 10px;
- position: relative;
- top: -3px;
- left: 4px;
+ display: inline-block;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ font-family: 'Font Awesome 5 Free';
+ font-weight: 900;
+ content: '\f05e';
+ color: white;
+ font-size: 10px;
+ position: relative;
+ top: -3px;
+ left: 4px;
}
.test-name-wrapper.conflicts .progress-indicator {
- background: #ffcd00;
+ background: #ffcd00;
}
.test-name-wrapper.conflicts .progress-indicator:before,
.test-name-wrapper.conflicts .progress-indicator:after {
- content: '';
- position: absolute;
- background: #976005;
- width: 2px;
- left: 8px;
+ content: '';
+ position: absolute;
+ background: #976005;
+ width: 2px;
+ left: 8px;
}
.test-name-wrapper.conflicts .progress-indicator:before {
- top: 3px;
- height: 7px;
+ top: 3px;
+ height: 7px;
}
.test-name-wrapper.conflicts .progress-indicator:after {
- top: 12px;
- height: 2px;
+ top: 12px;
+ height: 2px;
}
.test-name-wrapper.complete .progress-indicator {
- background: #1e8f37;
+ background: #1e8f37;
}
.test-name-wrapper.complete .progress-indicator:before,
.test-name-wrapper.complete .progress-indicator:after {
- content: '';
- position: absolute;
- background: white;
- -ms-transform: rotate(45deg); /* IE 9 */
- -webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
- transform: rotate(45deg);
+ content: '';
+ position: absolute;
+ background: white;
+ -ms-transform: rotate(45deg); /* IE 9 */
+ -webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
+ transform: rotate(45deg);
}
.test-name-wrapper.complete .progress-indicator:before {
- width: 4px;
- height: 3px;
- top: 8px;
- left: 4px;
+ width: 4px;
+ height: 3px;
+ top: 8px;
+ left: 4px;
}
.test-name-wrapper.complete .progress-indicator:after {
- width: 3px;
- height: 9px;
- top: 5px;
- left: 9px;
+ width: 3px;
+ height: 9px;
+ top: 5px;
+ left: 9px;
}
.test-iframe-container {
- padding: 0;
+ padding: 0;
}
main.container-fluid .test-iframe-container > .row {
- padding-top: 0;
+ padding-top: 0;
}
#test-iframe {
- width: 80vw;
- height: 65vh;
- border: 1px solid #d2d5d9;
- border-radius: 3px;
- padding: 0 1em;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
- 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
- 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+ width: 80vw;
+ height: 65vh;
+ border: 1px solid #d2d5d9;
+ border-radius: 3px;
+ padding: 0 1em;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
+ 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
+ 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}
.test-run-toolbar {
- display: flex;
- width: 100%;
+ display: flex;
+ width: 100%;
- padding-left: 0;
- padding-right: 0;
+ padding-left: 0;
+ padding-right: 0;
}
.test-run-toolbar li:first-of-type {
- flex-grow: 1;
+ flex-grow: 1;
}
.test-run-toolbar li:last-of-type {
- padding-right: 0;
+ padding-right: 0;
}
.dot {
- height: 10px;
- width: 10px;
- background-color: #6bda84;
- border-radius: 50%;
- display: inline-block;
+ height: 10px;
+ width: 10px;
+ background-color: #6bda84;
+ border-radius: 50%;
+ display: inline-block;
}
.test-navigator a[aria-current='true'] {
- color: black;
- font-weight: bold;
+ color: black;
+ font-weight: bold;
}
.test-navigator a[aria-current='true']:hover {
- text-decoration: none;
- color: black;
+ text-decoration: none;
+ color: black;
}
/* Test Headings and information */
.task-label {
- display: block;
- font-size: 0.6em;
- font-weight: normal;
- border-radius: 3px;
+ display: block;
+ font-size: 0.6em;
+ font-weight: normal;
+ border-radius: 3px;
}
.test-info-wrapper {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- flex-wrap: wrap;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ flex-wrap: wrap;
- padding: 0;
+ padding: 0;
}
.test-info-entity {
- padding: 0.5em 0.75em;
- border-radius: 3px;
- margin-bottom: 1em;
- font-size: 0.9em;
+ padding: 0.5em 0.75em;
+ border-radius: 3px;
+ margin-bottom: 1em;
+ font-size: 0.9em;
}
.apg-example-name,
@@ -324,156 +345,156 @@ main.container-fluid .test-iframe-container > .row {
.test-version,
.review-status,
.target-date {
- background: #f5f8fa;
- border: 1px solid #d2d5d9;
- width: 37%;
- text-align: center;
+ background: #f5f8fa;
+ border: 1px solid #d2d5d9;
+ width: 37%;
+ text-align: center;
}
.at-browser {
- display: flex;
- flex-direction: row;
- align-items: flex-start;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
- background: #f5f8fa;
- border: 1px solid #d2d5d9;
- width: 37%;
- /*text-align: center;*/
+ background: #f5f8fa;
+ border: 1px solid #d2d5d9;
+ width: 37%;
+ /*text-align: center;*/
}
.at-browser-row {
- width: 100%;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- justify-content: space-between;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: space-between;
}
#edit-fa-button {
- padding: 0;
- margin: 0;
- background: none;
- color: #919191;
- border: none;
+ padding: 0;
+ margin: 0;
+ background: none;
+ color: #919191;
+ border: none;
}
#edit-fa-button svg {
- padding: 0;
- margin: 0;
+ padding: 0;
+ margin: 0;
}
.tests-completed {
- width: 23.5%;
+ width: 23.5%;
}
.reviewing-as {
- background: #ecf6ff;
- border: 1px solid #c4d1e2;
- width: 100%;
+ background: #ecf6ff;
+ border: 1px solid #c4d1e2;
+ width: 100%;
}
.reviewing-as p {
- margin: 0;
- font-style: italic;
- margin-left: 0.5em;
- display: inline-block;
+ margin: 0;
+ font-style: italic;
+ margin-left: 0.5em;
+ display: inline-block;
}
.reviewing-as.bot {
- background: #f0e1ff;
- border: 1px solid #d29fff;
+ background: #f0e1ff;
+ border: 1px solid #d29fff;
}
/* Current Test Options */
.current-test-options {
- padding-right: 0;
+ padding-right: 0;
}
.current-test-options > div {
- background: #f5f8fa;
- border: 1px solid #d2d5d9;
- border-radius: 3px;
+ background: #f5f8fa;
+ border: 1px solid #d2d5d9;
+ border-radius: 3px;
}
.current-test-options h2 {
- margin-top: 0;
- padding: 0.9em;
- background: #e9ebee;
- font-size: 1em;
- font-weight: 700;
- border-bottom: 1px solid #d2d5d9;
- text-align: center;
+ margin-top: 0;
+ padding: 0.9em;
+ background: #e9ebee;
+ font-size: 1em;
+ font-weight: 700;
+ border-bottom: 1px solid #d2d5d9;
+ text-align: center;
}
.current-test-options .options-wrapper {
- padding: 0.05em 0.75em;
+ padding: 0.05em 0.75em;
}
.help-link {
- font-size: 0.9em;
- text-align: center;
- padding-top: 0.7em;
+ font-size: 0.9em;
+ text-align: center;
+ padding-top: 0.7em;
}
.fa-exclamation-circle,
.fa-redo,
.fa-pen,
.fa-external-link-alt {
- color: #959595;
+ color: #959595;
}
.fa-check {
- color: #1e8f37;
+ color: #1e8f37;
}
.status-bar {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
}
.at-browser-details-modal-alert {
- display: flex;
+ display: flex;
- grid-column: 1 / -1;
+ grid-column: 1 / -1;
}
fieldset .at-browser-details-modal-alert {
- margin-bottom: 0;
+ margin-bottom: 0;
}
.btn-options {
- width: 100%;
+ width: 100%;
}
a.btn-options:hover {
- text-decoration: none;
+ text-decoration: none;
}
.at-browser-details-modal-alert span {
- font-size: 0.875rem;
- line-height: 1.125rem;
+ font-size: 0.875rem;
+ line-height: 1.125rem;
}
.at-browser-details-full-column {
- grid-column: 1 / -1;
+ grid-column: 1 / -1;
}
.modal-header,
.modal-body,
.modal-footer {
- padding: 1.5rem 2.5rem;
+ padding: 1.5rem 2.5rem;
}
@media (min-width: 768px) {
- .modal-50w {
- min-width: 50%;
- }
+ .modal-50w {
+ min-width: 50%;
+ }
- .modal-60w {
- min-width: 60%;
- }
+ .modal-60w {
+ min-width: 60%;
+ }
}
.form-control.is-invalid {
- background-image: none;
+ background-image: none;
}
diff --git a/client/components/TestRun/index.jsx b/client/components/TestRun/index.jsx
index db7320fbf..27a7ce149 100644
--- a/client/components/TestRun/index.jsx
+++ b/client/components/TestRun/index.jsx
@@ -5,13 +5,11 @@ import useRouterQuery from '../../hooks/useRouterQuery';
import { useMutation, useQuery } from '@apollo/client';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
- faPen,
- faEdit,
- faRedo,
- faCheck,
- faCheckCircle,
- faExclamationCircle,
- faRobot
+ faPen,
+ faRedo,
+ faCheck,
+ faCheckCircle,
+ faExclamationCircle
} from '@fortawesome/free-solid-svg-icons';
import nextId from 'react-id-generator';
import { Alert, Button, Col, Container, Row } from 'react-bootstrap';
@@ -20,6 +18,7 @@ import ReviewConflictsModal from './ReviewConflictsModal';
import StatusBar from './StatusBar';
import TestRenderer from '../TestRenderer';
import OptionButton from './OptionButton';
+import Heading from './Heading';
import PageStatus from '../common/PageStatus';
import BasicModal from '../common/BasicModal';
import BasicThemedModal from '../common/BasicThemedModal';
@@ -28,1404 +27,1242 @@ import { useDetectUa } from '../../hooks/useDetectUa';
import DisplayNone from '../../utils/DisplayNone';
import { navigateTests } from '../../utils/navigateTests';
import {
- COLLECTION_JOB_STATUS_BY_TEST_PLAN_RUN_ID_QUERY,
- DELETE_TEST_RESULT_MUTATION,
- FIND_OR_CREATE_BROWSER_VERSION_MUTATION,
- FIND_OR_CREATE_TEST_RESULT_MUTATION,
- SAVE_TEST_RESULT_MUTATION,
- SUBMIT_TEST_RESULT_MUTATION,
- TEST_RUN_PAGE_ANON_QUERY,
- TEST_RUN_PAGE_QUERY
+ DELETE_TEST_RESULT_MUTATION,
+ FIND_OR_CREATE_BROWSER_VERSION_MUTATION,
+ FIND_OR_CREATE_TEST_RESULT_MUTATION,
+ SAVE_TEST_RESULT_MUTATION,
+ SUBMIT_TEST_RESULT_MUTATION,
+ TEST_RUN_PAGE_ANON_QUERY,
+ TEST_RUN_PAGE_QUERY
} from './queries';
import { evaluateAuth } from '../../utils/evaluateAuth';
import './TestRun.css';
import ReviewConflicts from '../ReviewConflicts';
import createIssueLink from '../../utils/createIssueLink';
import { convertDateToString } from '../../utils/formatter';
+import { Provider as CollectionJobContextProvider } from './CollectionJobContext';
const TestRun = () => {
- const params = useParams();
- const navigate = useNavigate();
- const routerQuery = useRouterQuery();
-
- // Detect UA information
- const { uaBrowser, uaMajor } = useDetectUa();
-
- const titleRef = useRef();
- // To prevent default AT/Browser versions being set before initial
- // AT & Browser Details Modal is saved
- const testRunStateRef = useRef();
- // HACK: Temporary fix to allow for consistency of TestRenderer after
- // testRunStateRef is nullified during unmount.
- // See 'unmount' of 'pageContent' hook in the TestRenderer component
- const recentTestRunStateRef = useRef();
- const testRunResultRef = useRef();
- const testRendererSubmitButtonRef = useRef();
- const conflictMarkdownRef = useRef();
- const adminReviewerCheckedRef = useRef(false);
- const adminReviewerOriginalTestRef = useRef();
- const editAtBrowserDetailsButtonRef = useRef();
-
- const { runId: testPlanRunId, testPlanReportId } = params;
-
- const { loading, data, error } = useQuery(
- testPlanRunId ? TEST_RUN_PAGE_QUERY : TEST_RUN_PAGE_ANON_QUERY,
- {
- fetchPolicy: 'cache-and-network',
- variables: { testPlanRunId, testPlanReportId }
- }
- );
-
- const { data: collectionJobQuery } = useQuery(
- COLLECTION_JOB_STATUS_BY_TEST_PLAN_RUN_ID_QUERY,
- {
- variables: { testPlanRunId },
- fetchPolicy: 'cache-and-network'
- }
- );
-
- const [createTestResult, { loading: createTestResultLoading }] =
- useMutation(FIND_OR_CREATE_TEST_RESULT_MUTATION);
- const [saveTestResult] = useMutation(SAVE_TEST_RESULT_MUTATION);
- const [submitTestResult] = useMutation(SUBMIT_TEST_RESULT_MUTATION);
- const [deleteTestResult] = useMutation(DELETE_TEST_RESULT_MUTATION);
- const [createBrowserVersion] = useMutation(
- FIND_OR_CREATE_BROWSER_VERSION_MUTATION
- );
+ const params = useParams();
+ const navigate = useNavigate();
+ const routerQuery = useRouterQuery();
+
+ // Detect UA information
+ const { uaBrowser, uaMajor } = useDetectUa();
+
+ const titleRef = useRef();
+ // To prevent default AT/Browser versions being set before initial
+ // AT & Browser Details Modal is saved
+ const testRunStateRef = useRef();
+ // HACK: Temporary fix to allow for consistency of TestRenderer after
+ // testRunStateRef is nullified during unmount.
+ // See 'unmount' of 'pageContent' hook in the TestRenderer component
+ const recentTestRunStateRef = useRef();
+ const testRunResultRef = useRef();
+ const testRendererSubmitButtonRef = useRef();
+ const conflictMarkdownRef = useRef();
+ const adminReviewerCheckedRef = useRef(false);
+ const adminReviewerOriginalTestRef = useRef();
+ const editAtBrowserDetailsButtonRef = useRef();
+
+ const { runId: testPlanRunId, testPlanReportId } = params;
+
+ const { loading, data, error } = useQuery(
+ testPlanRunId ? TEST_RUN_PAGE_QUERY : TEST_RUN_PAGE_ANON_QUERY,
+ {
+ fetchPolicy: 'cache-and-network',
+ variables: { testPlanRunId, testPlanReportId },
+ pollInterval: 0
+ }
+ );
+
+ useEffect(() => {
+ if (data) setup(data);
+ }, [data]);
+
+ const [createTestResult, { loading: createTestResultLoading }] = useMutation(
+ FIND_OR_CREATE_TEST_RESULT_MUTATION
+ );
+ const [saveTestResult] = useMutation(SAVE_TEST_RESULT_MUTATION);
+ const [submitTestResult] = useMutation(SUBMIT_TEST_RESULT_MUTATION);
+ const [deleteTestResult] = useMutation(DELETE_TEST_RESULT_MUTATION);
+ const [createBrowserVersion] = useMutation(
+ FIND_OR_CREATE_BROWSER_VERSION_MUTATION
+ );
+
+ const [isRendererReady, setIsRendererReady] = useState(false);
+ const [isSavingForm, setIsSavingForm] = useState(false);
+ const [isTestSubmitClicked, setIsTestSubmitClicked] = useState(false);
+ const [isTestEditClicked, setIsTestEditClicked] = useState(false);
+ const [showTestNavigator, setShowTestNavigator] = useState(true);
+ const [showStartOverModal, setShowStartOverModal] = useState(false);
+ const [showReviewConflictsModal, setShowReviewConflictsModal] =
+ useState(false);
+ const [showGetInvolvedModal, setShowGetInvolvedModal] = useState(false);
+
+ // Modal State Values
+ const [isShowingAtBrowserModal, setIsShowingAtBrowserModal] = useState(true);
+ const [showThemedModal, setShowThemedModal] = useState(false);
+ const [themedModalTitle, setThemedModalTitle] = useState('');
+ const [themedModalContent, setThemedModalContent] = useState(<>>);
+ const [themedModalOtherButton, setThemedModalOtherButton] = useState(null);
+ const [isEditAtBrowserDetailsModalClick, setIsEditAtBrowserDetailsClicked] =
+ useState(false);
+ const [updateMessageComponent, setUpdateMessageComponent] = useState(null);
+
+ // Queried State Values
+ const [testPlanRun, setTestPlanRun] = useState({});
+ const [users, setUsers] = useState([]);
+ const [tester, setTester] = useState();
+ const [tests, setTests] = useState([]);
+ const [testResults, setTestResults] = useState([]);
+ const [testPlanReport, setTestPlanReport] = useState({});
+ const [testPlanVersion, setTestPlanVersion] = useState();
+ const [currentTest, setCurrentTest] = useState({});
+ const [currentTestIndex, setCurrentTestIndex] = useState(0);
+ const [currentTestAtVersionId, setCurrentTestAtVersionId] = useState('');
+ const [currentTestBrowserVersionId, setCurrentTestBrowserVersionId] =
+ useState('');
+ const [currentAtVersion, setCurrentAtVersion] = useState('');
+ const [currentBrowserVersion, setCurrentBrowserVersion] = useState('');
+ const [pageReady, setPageReady] = useState(false);
+
+ const auth = evaluateAuth(data && data.me ? data.me : {});
+ let { id: userId, isSignedIn, isAdmin } = auth;
+
+ // if a signed in user navigates to this page, treat them as anon to prevent
+ // invalid save attempts
+ if (testPlanReportId) isSignedIn = false;
+
+ // check to ensure an admin that manually went to a test run url doesn't
+ // run the test as themselves
+ const openAsUserId =
+ routerQuery.get('user') || (tester && tester.id !== userId)
+ ? tester?.id
+ : null;
+ const testerId = openAsUserId || userId;
+ const isAdminReviewer = !!(isAdmin && openAsUserId);
+ const openAsUser = users?.find(user => user.id === openAsUserId);
+
+ useEffect(() => {
+ reset();
+
+ // Set up for the current test
+ const currentTest = tests[currentTestIndex];
+ if (currentTest) {
+ setPageReady(false);
+ if (isSignedIn) {
+ (async () => {
+ const { testPlanRun, testPlanReport } =
+ await createTestResultForRenderer(
+ currentTest.id,
+ currentTestAtVersionId,
+ currentTestBrowserVersionId
+ );
+ updateLocalState(testPlanRun, testPlanReport);
+ setPageReady(true);
+ })();
+ } else {
+ // To account for ANON viewing
+ setCurrentTest(tests[currentTestIndex]);
+ setPageReady(true);
+ }
+ } else if (data) setup(data);
+ }, [currentTestIndex]);
- const [isRendererReady, setIsRendererReady] = useState(false);
- const [isSavingForm, setIsSavingForm] = useState(false);
- const [isTestSubmitClicked, setIsTestSubmitClicked] = useState(false);
- const [isTestEditClicked, setIsTestEditClicked] = useState(false);
- const [showTestNavigator, setShowTestNavigator] = useState(true);
- const [showStartOverModal, setShowStartOverModal] = useState(false);
- const [showReviewConflictsModal, setShowReviewConflictsModal] =
- useState(false);
- const [showGetInvolvedModal, setShowGetInvolvedModal] = useState(false);
-
- // Modal State Values
- const [isShowingAtBrowserModal, setIsShowingAtBrowserModal] =
- useState(true);
- const [showThemedModal, setShowThemedModal] = useState(false);
- const [themedModalTitle, setThemedModalTitle] = useState('');
- const [themedModalContent, setThemedModalContent] = useState(<>>);
- const [themedModalOtherButton, setThemedModalOtherButton] = useState(null);
- const [isEditAtBrowserDetailsModalClick, setIsEditAtBrowserDetailsClicked] =
- useState(false);
- const [updateMessageComponent, setUpdateMessageComponent] = useState(null);
-
- // Queried State Values
- const [testPlanRun, setTestPlanRun] = useState({});
- const [users, setUsers] = useState([]);
- const [tester, setTester] = useState();
- const [tests, setTests] = useState([]);
- const [testResults, setTestResults] = useState([]);
- const [testPlanReport, setTestPlanReport] = useState({});
- const [testPlanVersion, setTestPlanVersion] = useState();
- const [currentTest, setCurrentTest] = useState({});
- const [currentTestIndex, setCurrentTestIndex] = useState(0);
- const [currentTestAtVersionId, setCurrentTestAtVersionId] = useState('');
- const [currentTestBrowserVersionId, setCurrentTestBrowserVersionId] =
- useState('');
- const [currentAtVersion, setCurrentAtVersion] = useState('');
- const [currentBrowserVersion, setCurrentBrowserVersion] = useState('');
- const [pageReady, setPageReady] = useState(false);
-
- const auth = evaluateAuth(data && data.me ? data.me : {});
- let { id: userId, isSignedIn, isAdmin } = auth;
+ const setup = data => {
+ const { testPlanRun, users } = data;
+ const { tester, testResults = [] } = testPlanRun || {};
+ let { testPlanReport } = testPlanRun || {};
// if a signed in user navigates to this page, treat them as anon to prevent
// invalid save attempts
if (testPlanReportId) isSignedIn = false;
-
- // check to ensure an admin that manually went to a test run url doesn't
- // run the test as themselves
- const openAsUserId =
- routerQuery.get('user') || (tester && tester.id !== userId)
- ? tester?.id
- : null;
- const testerId = openAsUserId || userId;
- const isAdminReviewer = !!(isAdmin && openAsUserId);
- const openAsUser = users?.find(user => user.id === openAsUserId);
-
- useEffect(() => {
- if (data) setup(data);
- }, [data]);
-
- useEffect(() => {
- reset();
-
- // Set up for the current test
- const currentTest = tests[currentTestIndex];
- if (currentTest) {
- setPageReady(false);
- if (isSignedIn) {
- (async () => {
- const { testPlanRun, testPlanReport } =
- await createTestResultForRenderer(
- currentTest.id,
- currentTestAtVersionId,
- currentTestBrowserVersionId
- );
- updateLocalState(testPlanRun, testPlanReport);
- setPageReady(true);
- })();
- } else {
- // To account for ANON viewing
- setCurrentTest(tests[currentTestIndex]);
- setPageReady(true);
- }
- } else if (data) setup(data);
- }, [currentTestIndex]);
-
- const setup = data => {
- const { testPlanRun, users } = data;
- const { tester, testResults = [] } = testPlanRun || {};
- let { testPlanReport } = testPlanRun || {};
-
- // if a signed in user navigates to this page, treat them as anon to prevent
- // invalid save attempts
- if (testPlanReportId) isSignedIn = false;
- if (!isSignedIn) testPlanReport = data.testPlanReport;
-
- const {
- testPlanVersion,
- runnableTests = [],
- conflicts = []
- } = testPlanReport || {};
-
- const tests = runnableTests.map((test, index) => ({
- ...test,
- index,
- seq: index + 1,
- testResult: testResults.find(t => t.test.id === test.id),
- hasConflicts: !!conflicts.find(c => c.source.test.id === test.id)
- }));
- const currentTest = tests[currentTestIndex];
-
- // Capture the AT & Browser Versions
- const defaultAtVersionId = testPlanReport.at.atVersions[0].id;
- const defaultBrowserVersionId =
- testPlanReport.browser.browserVersions[0].id;
-
- const currentTestAtVersionId =
- currentTest.testResult?.atVersion?.id || defaultAtVersionId;
-
- const currentTestBrowserVersionId =
- currentTest.testResult?.browserVersion?.id ||
- defaultBrowserVersionId;
-
- const currentAtVersion =
- currentTest.testResult?.atVersion ||
- testPlanReport.at.atVersions.find(
- item => item.id === currentTestAtVersionId
- ) ||
- 'N/A';
-
- let currentBrowserVersion =
- currentTest.testResult?.browserVersion ||
- testPlanReport.browser.browserVersions.find(
- item => item.id === currentTestBrowserVersionId
- ) ||
- 'N/A';
-
- // Only show major version of browser
- currentBrowserVersion = {
- id: currentAtVersion.id,
- name: currentBrowserVersion.name.split('.')[0]
- };
-
- // Auto batch the states
- setUsers(users);
- setTester(tester);
- setTestPlanRun(testPlanRun);
- setTestPlanReport(testPlanReport);
- setTestPlanVersion(testPlanVersion);
- setTests(tests);
- setTestResults(testResults);
- setCurrentTest(currentTest);
- setCurrentTestAtVersionId(currentTestAtVersionId);
- setCurrentTestBrowserVersionId(currentTestBrowserVersionId);
- setCurrentAtVersion(currentAtVersion);
- setCurrentBrowserVersion(currentBrowserVersion);
- // Testers do not need to change AT/Browser versions
- // while assigning verdicts for previously automated tests
- if (testPlanRun?.initiatedByAutomation) {
- setIsShowingAtBrowserModal(false);
- }
- setPageReady(true);
+ if (!isSignedIn) testPlanReport = data.testPlanReport;
+
+ const {
+ testPlanVersion,
+ runnableTests = [],
+ conflicts = []
+ } = testPlanReport || {};
+
+ const tests = runnableTests.map((test, index) => ({
+ ...test,
+ index,
+ seq: index + 1,
+ testResult: testResults.find(t => t.test.id === test.id),
+ hasConflicts: !!conflicts.find(c => c.source.test.id === test.id)
+ }));
+ const currentTest = tests[currentTestIndex];
+
+ // Capture the AT & Browser Versions
+ const defaultAtVersionId = testPlanReport.at.atVersions[0].id;
+ const defaultBrowserVersionId =
+ testPlanReport.browser.browserVersions[0].id;
+
+ const currentTestAtVersionId =
+ currentTest.testResult?.atVersion?.id || defaultAtVersionId;
+
+ const currentTestBrowserVersionId =
+ currentTest.testResult?.browserVersion?.id || defaultBrowserVersionId;
+
+ const currentAtVersion =
+ currentTest.testResult?.atVersion ||
+ testPlanReport.at.atVersions.find(
+ item => item.id === currentTestAtVersionId
+ ) ||
+ 'N/A';
+
+ let currentBrowserVersion =
+ currentTest.testResult?.browserVersion ||
+ testPlanReport.browser.browserVersions.find(
+ item => item.id === currentTestBrowserVersionId
+ ) ||
+ 'N/A';
+
+ // Only show major version of browser
+ currentBrowserVersion = {
+ id: currentAtVersion.id,
+ name: currentBrowserVersion.name.split('.')[0]
};
- const reset = () => {
- testRunStateRef.current = null;
- recentTestRunStateRef.current = null;
- testRunResultRef.current = null;
- conflictMarkdownRef.current = null;
-
- setPageReady(false);
- setIsRendererReady(false);
- setIsTestSubmitClicked(false);
-
- if (titleRef.current) titleRef.current.focus();
- };
-
- const updateLocalState = (updatedTestPlanRun, updatedTestPlanReport) => {
- const { conflicts, runnableTests } = updatedTestPlanReport;
-
- const testResults = updatedTestPlanRun.testResults;
- const tests = runnableTests.map((test, index) => ({
- ...test,
- index,
- seq: index + 1,
- testResult: testResults.find(t => t.test.id === test.id),
- hasConflicts: !!conflicts.find(c => c.source.test.id === test.id)
- }));
+ // Auto batch the states
+ setUsers(users);
+ setTester(tester);
+ setTestPlanRun(testPlanRun);
+ setTestPlanReport(testPlanReport);
+ setTestPlanVersion(testPlanVersion);
+ setTests(tests);
+ setTestResults(testResults);
+ setCurrentTest(currentTest);
+ setCurrentTestAtVersionId(currentTestAtVersionId);
+ setCurrentTestBrowserVersionId(currentTestBrowserVersionId);
+ setCurrentAtVersion(currentAtVersion);
+ setCurrentBrowserVersion(currentBrowserVersion);
+ // Testers do not need to change AT/Browser versions
+ // while assigning verdicts for previously automated tests
+ if (testPlanRun?.initiatedByAutomation) {
+ setIsShowingAtBrowserModal(false);
+ }
+ setPageReady(true);
+ };
+
+ const reset = () => {
+ testRunStateRef.current = null;
+ recentTestRunStateRef.current = null;
+ testRunResultRef.current = null;
+ conflictMarkdownRef.current = null;
+
+ setPageReady(false);
+ setIsRendererReady(false);
+ setIsTestSubmitClicked(false);
+
+ if (titleRef.current) titleRef.current.focus();
+ };
+
+ const updateLocalState = (updatedTestPlanRun, updatedTestPlanReport) => {
+ const { conflicts, runnableTests } = updatedTestPlanReport;
+
+ const testResults = updatedTestPlanRun.testResults;
+ const tests = runnableTests.map((test, index) => ({
+ ...test,
+ index,
+ seq: index + 1,
+ testResult: testResults.find(t => t.test.id === test.id),
+ hasConflicts: !!conflicts.find(c => c.source.test.id === test.id)
+ }));
+
+ setTests(tests);
+ setTestResults(testResults);
+ setCurrentTest(tests[currentTestIndex]);
+ setTestPlanReport({ ...testPlanReport, conflicts });
+ };
+
+ if (error) {
+ const { message } = error;
+ return (
+
+ );
+ }
- setTests(tests);
- setTestResults(testResults);
- setCurrentTest(tests[currentTestIndex]);
- setTestPlanReport({ ...testPlanReport, conflicts });
- };
+ if (!data || loading || createTestResultLoading || isSavingForm) {
+ return (
+
+ );
+ }
- if (error) {
- const { message } = error;
- return (
-
- );
+ if (isSignedIn && !testPlanRun) {
+ return (
+
+ );
+ }
+
+ const toggleTestNavigator = () => setShowTestNavigator(!showTestNavigator);
+
+ const createTestResultForRenderer = async (
+ testId,
+ atVersionId,
+ browserVersionId
+ ) => {
+ const result = await createTestResult({
+ variables: {
+ testPlanRunId,
+ testId,
+ atVersionId,
+ browserVersionId
+ }
+ });
+ return result.data.testPlanRun.findOrCreateTestResult;
+ };
+
+ // Check to see if there are tests to run
+ const testCount = tests.length;
+
+ // Check if this test is being run as an admin
+ if (
+ adminReviewerOriginalTestRef.current &&
+ adminReviewerOriginalTestRef.current !== currentTest.id
+ )
+ adminReviewerCheckedRef.current = false;
+
+ if (
+ isAdminReviewer &&
+ currentTest.testResult &&
+ !adminReviewerCheckedRef.current
+ )
+ adminReviewerOriginalTestRef.current = currentTest;
+
+ adminReviewerCheckedRef.current = true;
+
+ let issueLink;
+ const hasLoadingCompleted = Object.keys(currentTest).length;
+ if (hasLoadingCompleted) {
+ issueLink = createIssueLink({
+ testPlanTitle: testPlanVersion.title,
+ testPlanDirectory: testPlanVersion.testPlan.directory,
+ versionString: `V${convertDateToString(
+ testPlanVersion.updatedAt,
+ 'YY.MM.DD'
+ )}`,
+ testTitle: currentTest.title,
+ testRowNumber: currentTest.rowNumber,
+ testSequenceNumber: currentTest.seq,
+ testRenderedUrl: currentTest.renderedUrl,
+ atName: testPlanReport.at.name,
+ browserName: testPlanReport.browser.name,
+ atVersionName: currentAtVersion?.name,
+ browserVersionName: currentBrowserVersion?.name,
+ conflictMarkdown: conflictMarkdownRef.current
+ });
+ }
+
+ const remapScenarioResults = (
+ rendererState,
+ scenarioResults,
+ captureHighlightRequired = false
+ ) => {
+ let newScenarioResults = [];
+ if (!rendererState || !scenarioResults) {
+ throw new Error(
+ `Unable to merge invalid results:rendererState:${rendererState} | scenarioResults:${scenarioResults}`
+ );
}
- if (!data || loading || createTestResultLoading || isSavingForm) {
- return (
-
- );
+ const { commands } = rendererState;
+ if (!commands || commands.length !== scenarioResults.length) {
+ throw new Error(
+ `Unable to merge invalid results:commands:${commands} | commands.length !== scenarioResults.length:${
+ commands.length !== scenarioResults.length
+ }`
+ );
}
- if (isSignedIn && !testPlanRun) {
- return (
-
+ const UnexpectedBehaviorsArray = [
+ 'EXCESSIVELY_VERBOSE',
+ 'UNEXPECTED_CURSOR_POSITION',
+ 'SLUGGISH',
+ 'AT_CRASHED',
+ 'BROWSER_CRASHED',
+ 'OTHER'
+ ];
+
+ for (let i = 0; i < commands.length; i++) {
+ let scenarioResult = { ...scenarioResults[i] };
+ let assertionResults = [];
+ let unexpectedBehaviors = null;
+
+ // collect variables
+ const { atOutput, assertions, unexpected } = commands[i];
+
+ // process assertion results
+ for (let j = 0; j < assertions.length; j++) {
+ const { description, result, highlightRequired } = assertions[j];
+ const assertionResult = {
+ ...scenarioResult.assertionResults.find(
+ ({ assertion: { text } }) => text === description
+ ),
+ passed: result === 'pass'
+ };
+ assertionResults.push(
+ captureHighlightRequired
+ ? { ...assertionResult, highlightRequired }
+ : assertionResult
);
+ }
+
+ // process unexpected behaviors
+ const { hasUnexpected, behaviors, highlightRequired } = unexpected;
+ if (hasUnexpected === 'hasUnexpected') {
+ unexpectedBehaviors = [];
+ /**
+ * 0 = EXCESSIVELY_VERBOSE
+ * 1 = UNEXPECTED_CURSOR_POSITION
+ * 2 = SLUGGISH
+ * 3 = AT_CRASHED
+ * 4 = BROWSER_CRASHED
+ * 5 = OTHER
+ */
+ for (let i = 0; i < behaviors.length; i++) {
+ const behavior = behaviors[i];
+ if (behavior.checked) {
+ unexpectedBehaviors.push({
+ id: UnexpectedBehaviorsArray[i],
+ text: behavior.description,
+ details: behavior.more.value,
+ impact: behavior.impact.toUpperCase(),
+ highlightRequired: captureHighlightRequired
+ ? behavior.more.highlightRequired
+ : false
+ });
+ }
+ }
+ } else if (hasUnexpected === 'doesNotHaveUnexpected')
+ unexpectedBehaviors = [];
+
+ // re-assign scenario result due to read only values
+ scenarioResult.output = atOutput.value ? atOutput.value : null;
+ if (captureHighlightRequired)
+ scenarioResult.highlightRequired = atOutput.highlightRequired;
+ scenarioResult.assertionResults = [...assertionResults];
+ scenarioResult.unexpectedBehaviors = unexpectedBehaviors
+ ? [...unexpectedBehaviors]
+ : null;
+ if (captureHighlightRequired)
+ scenarioResult.unexpectedBehaviorHighlightRequired = highlightRequired;
+
+ newScenarioResults.push(scenarioResult);
}
- const toggleTestNavigator = () => setShowTestNavigator(!showTestNavigator);
-
- const createTestResultForRenderer = async (
- testId,
- atVersionId,
- browserVersionId
- ) => {
- const result = await createTestResult({
- variables: {
- testPlanRunId,
- testId,
- atVersionId,
- browserVersionId
- }
- });
- return result.data.testPlanRun.findOrCreateTestResult;
- };
-
- // Check to see if there are tests to run
- const hasTestsToRun = tests.length;
-
- // Check if this test is being run as an admin
- if (
- adminReviewerOriginalTestRef.current &&
- adminReviewerOriginalTestRef.current !== currentTest.id
- )
- adminReviewerCheckedRef.current = false;
+ return newScenarioResults;
+ };
+ const remapState = (rendererState, testResult) => {
if (
- isAdminReviewer &&
- currentTest.testResult &&
- !adminReviewerCheckedRef.current
+ !rendererState ||
+ !testResult.scenarioResults ||
+ rendererState.commands.length !== testResult.scenarioResults.length
)
- adminReviewerOriginalTestRef.current = currentTest;
-
- adminReviewerCheckedRef.current = true;
-
- let issueLink;
- const hasLoadingCompleted = Object.keys(currentTest).length;
- if (hasLoadingCompleted) {
- issueLink = createIssueLink({
- testPlanTitle: testPlanVersion.title,
- testPlanDirectory: testPlanVersion.testPlan.directory,
- versionString: `V${convertDateToString(
- testPlanVersion.updatedAt,
- 'YY.MM.DD'
- )}`,
- testTitle: currentTest.title,
- testRowNumber: currentTest.rowNumber,
- testSequenceNumber: currentTest.seq,
- testRenderedUrl: currentTest.renderedUrl,
- atName: testPlanReport.at.name,
- browserName: testPlanReport.browser.name,
- atVersionName: currentAtVersion?.name,
- browserVersionName: currentBrowserVersion?.name,
- conflictMarkdown: conflictMarkdownRef.current
- });
- }
+ return testResult;
- const remapScenarioResults = (
- rendererState,
- scenarioResults,
- captureHighlightRequired = false
+ const scenarioResults = remapScenarioResults(
+ rendererState,
+ testResult.scenarioResults,
+ true
+ );
+ return { ...testResult, scenarioResults };
+ };
+
+ const performButtonAction = async (action, index) => {
+ // TODO: Revise function
+ const saveForm = async (
+ withResult = false,
+ forceSave = false,
+ forceEdit = false
) => {
- let newScenarioResults = [];
- if (!rendererState || !scenarioResults) {
- throw new Error(
- `Unable to merge invalid results:rendererState:${rendererState} | scenarioResults:${scenarioResults}`
- );
- }
-
- const { commands } = rendererState;
- if (!commands || commands.length !== scenarioResults.length) {
- throw new Error(
- `Unable to merge invalid results:commands:${commands} | commands.length !== scenarioResults.length:${
- commands.length !== scenarioResults.length
- }`
- );
- }
-
- const UnexpectedBehaviorsArray = [
- 'EXCESSIVELY_VERBOSE',
- 'UNEXPECTED_CURSOR_POSITION',
- 'SLUGGISH',
- 'AT_CRASHED',
- 'BROWSER_CRASHED',
- 'OTHER'
- ];
-
- for (let i = 0; i < commands.length; i++) {
- let scenarioResult = { ...scenarioResults[i] };
- let assertionResults = [];
- let unexpectedBehaviors = null;
-
- // collect variables
- const { atOutput, assertions, unexpected } = commands[i];
-
- // process assertion results
- for (let j = 0; j < assertions.length; j++) {
- const { description, result, highlightRequired } =
- assertions[j];
- const assertionResult = {
- ...scenarioResult.assertionResults.find(
- ({ assertion: { text } }) => text === description
- ),
- passed: result === 'pass'
- };
- assertionResults.push(
- captureHighlightRequired
- ? { ...assertionResult, highlightRequired }
- : assertionResult
- );
- }
+ if (updateMessageComponent) {
+ setUpdateMessageComponent(null);
+ }
+ try {
+ if (forceEdit) setIsTestEditClicked(true);
+ else setIsTestEditClicked(false);
- // process unexpected behaviors
- const { hasUnexpected, behaviors, highlightRequired } = unexpected;
- if (hasUnexpected === 'hasUnexpected') {
- unexpectedBehaviors = [];
- /**
- * 0 = EXCESSIVELY_VERBOSE
- * 1 = UNEXPECTED_CURSOR_POSITION
- * 2 = SLUGGISH
- * 3 = AT_CRASHED
- * 4 = BROWSER_CRASHED
- * 5 = OTHER
- */
- for (let i = 0; i < behaviors.length; i++) {
- const behavior = behaviors[i];
- if (behavior.checked) {
- unexpectedBehaviors.push({
- id: UnexpectedBehaviorsArray[i],
- text: behavior.description,
- details: behavior.more.value,
- impact: behavior.impact.toUpperCase(),
- highlightRequired: captureHighlightRequired
- ? behavior.more.highlightRequired
- : false
- });
- }
- }
- } else if (hasUnexpected === 'doesNotHaveUnexpected')
- unexpectedBehaviors = [];
-
- // re-assign scenario result due to read only values
- scenarioResult.output = atOutput.value ? atOutput.value : null;
- if (captureHighlightRequired)
- scenarioResult.highlightRequired = atOutput.highlightRequired;
- scenarioResult.assertionResults = [...assertionResults];
- scenarioResult.unexpectedBehaviors = unexpectedBehaviors
- ? [...unexpectedBehaviors]
- : null;
- if (captureHighlightRequired)
- scenarioResult.unexpectedBehaviorHighlightRequired =
- highlightRequired;
-
- newScenarioResults.push(scenarioResult);
- }
-
- return newScenarioResults;
- };
-
- const remapState = (rendererState, testResult) => {
- if (
- !rendererState ||
- !testResult.scenarioResults ||
- rendererState.commands.length !== testResult.scenarioResults.length
- )
- return testResult;
+ if (!isSignedIn) return true;
+ if (!forceEdit && currentTest.testResult?.completedAt) return true;
+ setIsSavingForm(true);
const scenarioResults = remapScenarioResults(
- rendererState,
- testResult.scenarioResults,
- true
+ testRunStateRef.current || recentTestRunStateRef.current,
+ currentTest.testResult?.scenarioResults,
+ false
);
- return { ...testResult, scenarioResults };
- };
- const performButtonAction = async (action, index) => {
- // TODO: Revise function
- const saveForm = async (
- withResult = false,
- forceSave = false,
- forceEdit = false
- ) => {
- if (updateMessageComponent) {
- setUpdateMessageComponent(null);
- }
- try {
- if (forceEdit) setIsTestEditClicked(true);
- else setIsTestEditClicked(false);
-
- if (!isSignedIn) return true;
- if (!forceEdit && currentTest.testResult?.completedAt)
- return true;
-
- setIsSavingForm(true);
- const scenarioResults = remapScenarioResults(
- testRunStateRef.current || recentTestRunStateRef.current,
- currentTest.testResult?.scenarioResults,
- false
- );
-
- await handleSaveOrSubmitTestResultAction(
- {
- atVersionId: currentTestAtVersionId,
- browserVersionId: currentTestBrowserVersionId,
- scenarioResults
- },
- forceSave ? false : !!testRunResultRef.current
- );
-
- if (withResult && !forceSave) {
- setIsSavingForm(false);
- return !!testRunResultRef.current;
- }
-
- setIsSavingForm(false);
- return true;
- } catch (e) {
- console.error('save.error', e);
- setIsSavingForm(false);
- }
- };
+ await handleSaveOrSubmitTestResultAction(
+ {
+ atVersionId: currentTestAtVersionId,
+ browserVersionId: currentTestBrowserVersionId,
+ scenarioResults
+ },
+ forceSave ? false : !!testRunResultRef.current
+ );
- switch (action) {
- case 'goToTestAtIndex': {
- // Save renderer's form state
- await saveForm(false, true);
- setCurrentTestIndex(index);
- break;
- }
- case 'goToNextTest': {
- // Save renderer's form state
- await saveForm(false, true);
- navigateTests(false, currentTest, tests, setCurrentTestIndex);
- break;
- }
- case 'goToPreviousTest': {
- // Save renderer's form state
- await saveForm(false, true);
- navigateTests(true, currentTest, tests, setCurrentTestIndex);
- break;
- }
- case 'editTest': {
- testRunResultRef.current = null;
- await saveForm(false, true, true);
- if (titleRef.current) titleRef.current.focus();
- break;
- }
- case 'saveTest': {
- if (!isSignedIn) {
- setShowGetInvolvedModal(true);
- break;
- }
- if (testRendererSubmitButtonRef.current) {
- testRendererSubmitButtonRef.current.click();
- setIsTestSubmitClicked(true);
-
- // check to see if form was successfully submitted, if so, return to top of summary document
- const forceFocusOnSave = await saveForm(true);
- if (forceFocusOnSave)
- if (titleRef.current) titleRef.current.focus();
- }
- break;
- }
- case 'closeTest': {
- // Save renderer's form state
- await saveForm();
- navigate('/test-queue');
- break;
- }
+ if (withResult && !forceSave) {
+ setIsSavingForm(false);
+ return !!testRunResultRef.current;
}
+
+ setIsSavingForm(false);
+ return true;
+ } catch (e) {
+ console.error('save.error', e);
+ setIsSavingForm(false);
+ }
};
- const handleTestClick = async index =>
- await performButtonAction('goToTestAtIndex', index);
+ switch (action) {
+ case 'goToTestAtIndex': {
+ // Save renderer's form state
+ await saveForm(false, true);
+ setCurrentTestIndex(index);
+ break;
+ }
+ case 'goToNextTest': {
+ // Save renderer's form state
+ await saveForm(false, true);
+ navigateTests(false, currentTest, tests, setCurrentTestIndex);
+ break;
+ }
+ case 'goToPreviousTest': {
+ // Save renderer's form state
+ await saveForm(false, true);
+ navigateTests(true, currentTest, tests, setCurrentTestIndex);
+ break;
+ }
+ case 'editTest': {
+ testRunResultRef.current = null;
+ await saveForm(false, true, true);
+ if (titleRef.current) titleRef.current.focus();
+ break;
+ }
+ case 'saveTest': {
+ if (!isSignedIn) {
+ setShowGetInvolvedModal(true);
+ break;
+ }
+ if (testRendererSubmitButtonRef.current) {
+ testRendererSubmitButtonRef.current.click();
+ setIsTestSubmitClicked(true);
- const handleSaveClick = async () => performButtonAction('saveTest');
+ // check to see if form was successfully submitted, if so, return to top of summary document
+ const forceFocusOnSave = await saveForm(true);
+ if (forceFocusOnSave) if (titleRef.current) titleRef.current.focus();
+ }
+ break;
+ }
+ case 'closeTest': {
+ // Save renderer's form state
+ await saveForm();
+ navigate('/test-queue');
+ break;
+ }
+ }
+ };
- const handleNextTestClick = async () => performButtonAction('goToNextTest');
+ const handleTestClick = async index =>
+ await performButtonAction('goToTestAtIndex', index);
- const handlePreviousTestClick = async () =>
- performButtonAction('goToPreviousTest');
+ const handleSaveClick = async () => performButtonAction('saveTest');
- const handleCloseRunClick = async () => performButtonAction('closeTest');
+ const handleNextTestClick = async () => performButtonAction('goToNextTest');
- const handleEditResultsClick = async () => performButtonAction('editTest');
+ const handlePreviousTestClick = async () =>
+ performButtonAction('goToPreviousTest');
- const handleStartOverButtonClick = async () => setShowStartOverModal(true);
+ const handleCloseRunClick = async () => performButtonAction('closeTest');
- const handleStartOverAction = async () => {
- const { id } = currentTest.testResult;
- let variables = {
- id
- };
- await deleteTestResult({ variables });
+ const handleEditResultsClick = async () => performButtonAction('editTest');
- reset();
- setPageReady(false);
- const { testPlanRun, testPlanReport } =
- await createTestResultForRenderer(
- currentTest.id,
- currentTestAtVersionId,
- currentTestBrowserVersionId
- );
- updateLocalState(testPlanRun, testPlanReport);
- setPageReady(true);
+ const handleStartOverButtonClick = async () => setShowStartOverModal(true);
- // close modal after action
- setShowStartOverModal(false);
+ const handleStartOverAction = async () => {
+ const { id } = currentTest.testResult;
+ let variables = {
+ id
};
-
- const handleSaveOrSubmitTestResultAction = async (
- { atVersionId, browserVersionId, scenarioResults = [] },
- isSubmit = false
- ) => {
- const { id } = currentTest.testResult;
-
- /*
- * The shape of scenarioResults should be:
- *
- * {
- * ..id,
- * ..output,
- * ..assertionResults: [
- * ....{
- * ......id
- * ......passed
- * ....},
- * ....other assertionResults,
- * ..],
- * ..unexpectedBehaviors: [
- * ....{
- * ......id
- * ......impact
- * ......details
- * ....},
- * ....other unexpectedBehaviors,
- * ..]
- * }
- * */
- const formattedScenarioResults = scenarioResults.map(
- ({ assertionResults, id, output, unexpectedBehaviors }) => ({
- id,
- output: output,
- unexpectedBehaviors: unexpectedBehaviors?.map(
- ({ id, impact, details }) => ({
- id,
- impact,
- details
- })
- ),
- assertionResults: assertionResults
- // All assertions are always being passed from the TestRenderer results, but
- // when there is a 0-priority assertion exception, an id won't be provided,
- // so do not include that result.
- // This is due to the TestRenderer still requiring the position of the
- // excluded assertion, but it can be removed at this point before being passed
- // to the server
- .filter(el => !!el.id)
- .map(({ id, passed }) => ({
- id,
- passed
- }))
- })
- );
-
- let variables = {
+ await deleteTestResult({ variables });
+
+ reset();
+ setPageReady(false);
+ const { testPlanRun, testPlanReport } = await createTestResultForRenderer(
+ currentTest.id,
+ currentTestAtVersionId,
+ currentTestBrowserVersionId
+ );
+ updateLocalState(testPlanRun, testPlanReport);
+ setPageReady(true);
+
+ // close modal after action
+ setShowStartOverModal(false);
+ };
+
+ const handleSaveOrSubmitTestResultAction = async (
+ { atVersionId, browserVersionId, scenarioResults = [] },
+ isSubmit = false
+ ) => {
+ const { id } = currentTest.testResult;
+
+ /*
+ * The shape of scenarioResults should be:
+ *
+ * {
+ * ..id,
+ * ..output,
+ * ..assertionResults: [
+ * ....{
+ * ......id
+ * ......passed
+ * ....},
+ * ....other assertionResults,
+ * ..],
+ * ..unexpectedBehaviors: [
+ * ....{
+ * ......id
+ * ......impact
+ * ......details
+ * ....},
+ * ....other unexpectedBehaviors,
+ * ..]
+ * }
+ * */
+ const formattedScenarioResults = scenarioResults.map(
+ ({ assertionResults, id, output, unexpectedBehaviors }) => ({
+ id,
+ output: output,
+ unexpectedBehaviors: unexpectedBehaviors?.map(
+ ({ id, impact, details }) => ({
id,
- atVersionId,
- browserVersionId,
- scenarioResults: formattedScenarioResults
- };
+ impact,
+ details
+ })
+ ),
+ assertionResults: assertionResults
+ // All assertions are always being passed from the TestRenderer results, but
+ // when there is a 0-priority assertion exception, an id won't be provided,
+ // so do not include that result.
+ // This is due to the TestRenderer still requiring the position of the
+ // excluded assertion, but it can be removed at this point before being passed
+ // to the server
+ .filter(el => !!el.id)
+ .map(({ id, passed }) => ({
+ id,
+ passed
+ }))
+ })
+ );
- if (isSubmit) {
- const result = await submitTestResult({ variables });
- const { testPlanRun, testPlanReport } =
- result.data.testResult.submitTestResult;
- updateLocalState(testPlanRun, testPlanReport);
- } else {
- const result = await saveTestResult({ variables });
- const { testPlanRun, testPlanReport } =
- result.data.testResult.saveTestResult;
- updateLocalState(testPlanRun, testPlanReport);
- }
+ let variables = {
+ id,
+ atVersionId,
+ browserVersionId,
+ scenarioResults: formattedScenarioResults
};
- const handleReviewConflictsButtonClick = async () =>
- setShowReviewConflictsModal(true);
-
- const handleEditAtBrowserDetailsClick = async () => {
- setIsEditAtBrowserDetailsClicked(true);
-
- if (isAdminReviewer && adminReviewerOriginalTestRef.current) {
- if (testPlanReport.browser.name !== uaBrowser) {
- setThemedModalTitle(
- 'Your Browser is different than the one used to record this result'
- );
- setThemedModalContent(
- <>
- You are currently using{' '}
-
- {uaBrowser} {uaMajor}
-
- , but are trying to edit a test result that was
- submitted with{' '}
-
- {testPlanReport.browser.name}{' '}
- {
- adminReviewerOriginalTestRef.current.testResult
- .browserVersion.name
- }
-
- .
-
- You can't change the Browser type but can make
- other changes. Please proceed with caution.
- >
- );
- setThemedModalOtherButton(null);
- setShowThemedModal(true);
- return;
- }
+ if (isSubmit) {
+ const result = await submitTestResult({ variables });
+ const { testPlanRun, testPlanReport } =
+ result.data.testResult.submitTestResult;
+ updateLocalState(testPlanRun, testPlanReport);
+ } else {
+ const result = await saveTestResult({ variables });
+ const { testPlanRun, testPlanReport } =
+ result.data.testResult.saveTestResult;
+ updateLocalState(testPlanRun, testPlanReport);
+ }
+ };
- if (
- currentTest.testResult?.atVersion?.name !==
- adminReviewerOriginalTestRef.current.testResult?.atVersion?.name
- ) {
- setThemedModalTitle(
- 'Your AT Version is different than the one used to record this result'
- );
- setThemedModalContent(
- <>
- You are currently running{' '}
-
- {testPlanReport.at.name}{' '}
- {currentTest.testResult?.atVersion?.name}
-
- , but are editing a test result that was submitted with{' '}
-
- {testPlanReport.at.name}{' '}
- {
- adminReviewerOriginalTestRef.current.testResult
- ?.atVersion?.name
- }
-
- .
-
- Do you want to update the AT version used to record this
- test result?
- >
- );
- setThemedModalOtherButton({
- text: 'Update AT Version',
- action: () => {
- setShowThemedModal(false);
- setIsShowingAtBrowserModal(true);
- }
- });
- setShowThemedModal(true);
- return;
- }
+ const handleReviewConflictsButtonClick = async () =>
+ setShowReviewConflictsModal(true);
- if (
- !adminReviewerOriginalTestRef.current.testResult?.browserVersion?.name.includes(
- `${uaMajor}`
- )
- ) {
- setThemedModalTitle(
- 'Your Browser Version is different than the one used to record this result'
- );
- setThemedModalContent(
- <>
- You are currently using{' '}
-
- {uaBrowser} {uaMajor}
-
- , but are trying to edit a test result that was
- submitted with{' '}
-
- {testPlanReport.browser.name}{' '}
- {
- adminReviewerOriginalTestRef.current.testResult
- ?.browserVersion?.name
- }
-
- .
-
- Do you want to update the Browser version used to record
- this test result?
- >
- );
- setThemedModalOtherButton({
- text: 'Update Browser Version',
- action: () => {
- setShowThemedModal(false);
- setIsShowingAtBrowserModal(true);
- }
- });
- setShowThemedModal(true);
- return;
- }
- }
- setIsShowingAtBrowserModal(true);
- };
+ const handleEditAtBrowserDetailsClick = async () => {
+ setIsEditAtBrowserDetailsClicked(true);
- const handleAtAndBrowserDetailsModalAction = async (
- updatedAtVersionName,
- updatedBrowserVersionName,
- updateMessage
- ) => {
- // Get version id for selected atVersion and browserVersion from name
- const atVersion = testPlanReport.at.atVersions.find(
- item => item.name === updatedAtVersionName
+ if (isAdminReviewer && adminReviewerOriginalTestRef.current) {
+ if (testPlanReport.browser.name !== uaBrowser) {
+ setThemedModalTitle(
+ 'Your Browser is different than the one used to record this result'
);
-
- let browserVersion = testPlanReport.browser.browserVersions.find(
- item => item.name === updatedBrowserVersionName
+ setThemedModalContent(
+ <>
+ You are currently using{' '}
+
+ {uaBrowser} {uaMajor}
+
+ , but are trying to edit a test result that was submitted with{' '}
+
+ {testPlanReport.browser.name}{' '}
+ {
+ adminReviewerOriginalTestRef.current.testResult.browserVersion
+ .name
+ }
+
+ .
+
+ You can't change the Browser type but can make other changes.
+ Please proceed with caution.
+ >
);
-
- // create version if not exists (accounting for admin providing new versions)
- if (!browserVersion) {
- const createBrowserVersionResult = await createBrowserVersion({
- variables: {
- browserId: testPlanReport.browser.id,
- browserVersionName: updatedBrowserVersionName
- }
- });
- browserVersion =
- createBrowserVersionResult.data?.browser
- ?.findOrCreateBrowserVersion;
- }
-
- // Only show major browser version
- browserVersion = {
- id: browserVersion.id,
- name: browserVersion.name.split('.')[0]
- };
-
- const updateMessageComponent = updateMessage ? (
- <>
-
- {updateMessage}
- >
- ) : null;
-
- setCurrentTestAtVersionId(atVersion.id);
- setCurrentTestBrowserVersionId(browserVersion.id);
- setCurrentAtVersion(atVersion);
- setCurrentBrowserVersion(browserVersion);
- setUpdateMessageComponent(updateMessageComponent);
-
- const { testPlanRun: _testPlanRun, testPlanReport: _testPlanReport } =
- await createTestResultForRenderer(
- currentTest.id,
- atVersion.id,
- browserVersion.id
- );
- updateLocalState(_testPlanRun, _testPlanReport);
- handleAtAndBrowserDetailsModalCloseAction();
- };
-
- const handleAtAndBrowserDetailsModalCloseAction = () => {
- setIsShowingAtBrowserModal(false);
- if (isEditAtBrowserDetailsModalClick)
- editAtBrowserDetailsButtonRef.current.focus();
- };
-
- const onThemedModalClose = () => {
- setShowThemedModal(false);
- editAtBrowserDetailsButtonRef.current.focus();
- };
-
- const renderTestsCompletedInfoBox = () => {
- let isReviewingBot = false;
- if (openAsUserId) {
- isReviewingBot = openAsUser.isBot;
- }
-
- let content;
-
- if (isReviewingBot) {
- content = (
- <>
- {`${testResults.reduce(
- (acc, { scenarioResults }) =>
- acc +
- (scenarioResults &&
- scenarioResults.every(({ output }) => !!output)
- ? 1
- : 0),
- 0
- )} of ${tests.length}`} {' '}
- responses collected.
- >
- );
- } else if (!isSignedIn) {
- content = {tests.length} tests to view ;
- } else if (hasTestsToRun) {
- content = (
- <>
- {' '}
- {`${testResults.reduce(
- (acc, { completedAt }) => acc + (completedAt ? 1 : 0),
- 0
- )} of ${tests.length}`} {' '}
- tests completed
- >
- );
- } else {
- content = No tests for this AT and Browser combination
;
- }
- return (
-
+ setThemedModalOtherButton(null);
+ setShowThemedModal(true);
+ return;
+ }
+
+ if (
+ currentTest.testResult?.atVersion?.name !==
+ adminReviewerOriginalTestRef.current.testResult?.atVersion?.name
+ ) {
+ setThemedModalTitle(
+ 'Your AT Version is different than the one used to record this result'
);
- };
-
- const renderTestContent = (testPlanReport, currentTest, heading) => {
- const { index } = currentTest;
- const isComplete = currentTest.testResult
- ? !!currentTest.testResult.completedAt
- : false;
- const isFirstTest = index === 0;
- const isLastTest = currentTest.seq === tests.length;
-
- let primaryButtons; // These are the list of buttons that will appear below the tests
- let forwardButtons = []; // These are buttons that navigate to next tests and continue
-
- const nextButton = (
-
- Next Test
-
+ setThemedModalContent(
+ <>
+ You are currently running{' '}
+
+ {testPlanReport.at.name} {currentTest.testResult?.atVersion?.name}
+
+ , but are editing a test result that was submitted with{' '}
+
+ {testPlanReport.at.name}{' '}
+ {adminReviewerOriginalTestRef.current.testResult?.atVersion?.name}
+
+ .
+
+ Do you want to update the AT version used to record this test
+ result?
+ >
);
+ setThemedModalOtherButton({
+ text: 'Update AT Version',
+ action: () => {
+ setShowThemedModal(false);
+ setIsShowingAtBrowserModal(true);
+ }
+ });
+ setShowThemedModal(true);
+ return;
+ }
- const previousButton = (
-
- Previous Test
-
+ if (
+ !adminReviewerOriginalTestRef.current.testResult?.browserVersion?.name.includes(
+ `${uaMajor}`
+ )
+ ) {
+ setThemedModalTitle(
+ 'Your Browser Version is different than the one used to record this result'
);
+ setThemedModalContent(
+ <>
+ You are currently using{' '}
+
+ {uaBrowser} {uaMajor}
+
+ , but are trying to edit a test result that was submitted with{' '}
+
+ {testPlanReport.browser.name}{' '}
+ {
+ adminReviewerOriginalTestRef.current.testResult?.browserVersion
+ ?.name
+ }
+
+ .
+
+ Do you want to update the Browser version used to record this test
+ result?
+ >
+ );
+ setThemedModalOtherButton({
+ text: 'Update Browser Version',
+ action: () => {
+ setShowThemedModal(false);
+ setIsShowingAtBrowserModal(true);
+ }
+ });
+ setShowThemedModal(true);
+ return;
+ }
+ }
+ setIsShowingAtBrowserModal(true);
+ };
+
+ const handleAtAndBrowserDetailsModalAction = async (
+ updatedAtVersionName,
+ updatedBrowserVersionName,
+ updateMessage
+ ) => {
+ // Get version id for selected atVersion and browserVersion from name
+ const atVersion = testPlanReport.at.atVersions.find(
+ item => item.name === updatedAtVersionName
+ );
- if (isComplete) {
- const editButton = (
-
-
- Edit Results
-
- );
-
- const continueButton = (
-
- Continue
-
- );
+ let browserVersion = testPlanReport.browser.browserVersions.find(
+ item => item.name === updatedBrowserVersionName
+ );
- if (!isLastTest) forwardButtons = [nextButton];
- primaryButtons = [
- previousButton,
- editButton,
- ...forwardButtons,
- continueButton
- ];
- } else {
- // same key to maintain focus
- const saveResultsButton = (
-
- Submit Results
-
- );
- if (!isLastTest) forwardButtons = [nextButton];
- primaryButtons = [
- previousButton,
- ...forwardButtons,
- saveResultsButton
- ];
+ // create version if not exists (accounting for admin providing new versions)
+ if (!browserVersion) {
+ const createBrowserVersionResult = await createBrowserVersion({
+ variables: {
+ browserId: testPlanReport.browser.id,
+ browserVersionName: updatedBrowserVersionName
}
+ });
+ browserVersion =
+ createBrowserVersionResult.data?.browser?.findOrCreateBrowserVersion;
+ }
- const externalLogsUrl =
- collectionJobQuery?.collectionJobByTestPlanRunId?.externalLogsUrl;
-
- const menuRightOfContent = (
-
-
Test Options
-
-
-
- }
- target="_blank"
- href={issueLink}
- />
-
- {openAsUser?.isBot && externalLogsUrl ? (
-
-
-
- ) : (
-
-
- }
- onClick={handleStartOverButtonClick}
- disabled={!isSignedIn}
- />
-
- )}
-
-
-
-
-
-
- Email us if you need help
-
-
-
-
- );
-
- return (
- <>
-
- Test {currentTest.seq}:
- {currentTest.title}
-
- {heading}
-
- {pageReady && (isSignedIn ? currentTest.testResult : true) && (
-
-
-
-
-
- {isRendererReady && (
-
-
-
- {primaryButtons.map(button => (
- {button}
- ))}
-
-
- )}
-
-
- {menuRightOfContent}
-
-
- )}
-
-
-
-
- {/* Modals */}
- {showStartOverModal && (
- setShowStartOverModal(false)}
- />
- )}
- {showGetInvolvedModal && (
-
- Only members of the ARIA-AT test team can submit
- data. If you fill in this form, your data will
- not be saved! Check out the{' '}
- home page to learn more about
- how to get involved.
- >
- }
- closeLabel="Close"
- handleClose={() => setShowGetInvolvedModal(false)}
- />
- )}
- {showReviewConflictsModal && (
- setShowReviewConflictsModal(false)}
- />
- )}
- >
- );
+ // Only show major browser version
+ browserVersion = {
+ id: browserVersion.id,
+ name: browserVersion.name.split('.')[0]
};
- let heading;
- let content;
- let openAsUserHeading = null;
-
- if (openAsUserId) {
- if (openAsUser.isBot) {
- openAsUserHeading = (
-
- Reviewing tests of{' '}
- {' '}
- {`${openAsUser.username}`}.
-
- );
- } else {
- openAsUserHeading = (
-
- Reviewing tests of
{`${openAsUser.username}`}.
-
{`All changes will be saved as performed by ${openAsUser.username}.`}
-
- );
- }
- }
+ const updateMessageComponent = updateMessage ? (
+ <>
+
+ {updateMessage}
+ >
+ ) : null;
+
+ setCurrentTestAtVersionId(atVersion.id);
+ setCurrentTestBrowserVersionId(browserVersion.id);
+ setCurrentAtVersion(atVersion);
+ setCurrentBrowserVersion(browserVersion);
+ setUpdateMessageComponent(updateMessageComponent);
+
+ const { testPlanRun: _testPlanRun, testPlanReport: _testPlanReport } =
+ await createTestResultForRenderer(
+ currentTest.id,
+ atVersion.id,
+ browserVersion.id
+ );
+ updateLocalState(_testPlanRun, _testPlanReport);
+ handleAtAndBrowserDetailsModalCloseAction();
+ };
+
+ const handleAtAndBrowserDetailsModalCloseAction = () => {
+ setIsShowingAtBrowserModal(false);
+ if (isEditAtBrowserDetailsModalClick)
+ editAtBrowserDetailsButtonRef.current.focus();
+ };
+
+ const onThemedModalClose = () => {
+ setShowThemedModal(false);
+ editAtBrowserDetailsButtonRef.current.focus();
+ };
+
+ const renderTestContent = (testPlanReport, currentTest, heading) => {
+ const { index } = currentTest;
+ const isComplete = currentTest.testResult
+ ? !!currentTest.testResult.completedAt
+ : false;
+ const isFirstTest = index === 0;
+ const isLastTest = currentTest.seq === tests.length;
+
+ let primaryButtons; // These are the list of buttons that will appear below the tests
+ let forwardButtons = []; // These are buttons that navigate to next tests and continue
+
+ const nextButton = (
+
+ Next Test
+
+ );
- heading = pageReady && (
- <>
-
-
-
- Test Plan: {' '}
- {`${
- testPlanVersion.title ||
- testPlanVersion.testPlan?.directory ||
- ''
- }`}
-
-
-
-
-
- AT: {' '}
- {`${testPlanReport.at?.name}${
- isSignedIn ? ` ${currentAtVersion?.name}` : ''
- }`}
-
-
- Browser: {' '}
- {`${testPlanReport.browser?.name}${
- isSignedIn
- ? ` ${currentBrowserVersion?.name || ''}`
- : ''
- }`}
-
-
- {isSignedIn && (
-
-
-
- )}
-
- {renderTestsCompletedInfoBox()}
-
- {openAsUserHeading}
- >
+ const previousButton = (
+
+ Previous Test
+
);
- if (!isSignedIn || !testPlanRun?.isComplete) {
- content = hasTestsToRun ? (
- renderTestContent(testPlanReport, currentTest, heading)
- ) : (
- // No tests loaded
- <>
- {heading}
- No tests for this At and Browser combination
- >
- );
+ if (isComplete) {
+ const editButton = (
+
+
+ Edit Results
+
+ );
+
+ const continueButton = (
+
+ Continue
+
+ );
+
+ if (!isLastTest) forwardButtons = [nextButton];
+ primaryButtons = [
+ previousButton,
+ editButton,
+ ...forwardButtons,
+ continueButton
+ ];
} else {
- content = (
-
- {heading}
-
-
- Thanks! Your results
- have been submitted. Please return to the{' '}
- Test Queue.
-
-
-
- );
+ // same key to maintain focus
+ const saveResultsButton = (
+
+ Submit Results
+
+ );
+ if (!isLastTest) forwardButtons = [nextButton];
+ primaryButtons = [previousButton, ...forwardButtons, saveResultsButton];
}
+ const externalLogsUrl = testPlanRun?.collectionJob?.externalLogsUrl;
+
+ const menuRightOfContent = (
+
+
Test Options
+
+
+
+ }
+ target="_blank"
+ href={issueLink}
+ />
+
+ {openAsUser?.isBot && externalLogsUrl ? (
+
+
+
+ ) : (
+
+ }
+ onClick={handleStartOverButtonClick}
+ disabled={!isSignedIn}
+ />
+
+ )}
+
+
+
+
+
+ Email us if you need help
+
+
+
+ );
+
+ // we are ready enough to show the page and all the buttons when the above code is
+ // pageReady and we have an anon view, bot test run, or a test result to display
+ const completeRender = pageReady && (!isSignedIn || currentTest.testResult);
+
return (
- pageReady && (
-
-
-
- {hasTestsToRun
- ? `${currentTest.title} for ${testPlanReport.at?.name} ${currentAtVersion?.name} and ${testPlanReport.browser?.name} ${currentBrowserVersion?.name} ` +
- `| ARIA-AT`
- : 'No tests for this AT and Browser | ARIA-AT'}
-
-
- {updateMessageComponent && (
-
- {updateMessageComponent}
-
- )}
+ <>
+
+ Test {currentTest.seq}:
+ {currentTest.title}
+
+ {heading}
+
+ {completeRender && (
+
+
+
+
+
+ {isRendererReady && (
-
-
-
- {content}
-
-
+
+
+ {primaryButtons.map(button => (
+ {button}
+ ))}
+
- {showThemedModal && (
-
- )}
- {isSignedIn && isShowingAtBrowserModal && (
- {
- // Only provide at version options that released
- // at the same time or later than the minimum
- // AT version
- let earliestReleasedAt = null;
- if (testPlanReport.minimumAtVersion) {
- earliestReleasedAt = new Date(
- testPlanReport.minimumAtVersion.releasedAt
- );
- return (
- new Date(item.releasedAt) >=
- earliestReleasedAt
- );
- }
- return item;
- })
- .map(item => item.name)}
- browserName={testPlanReport.browser.name}
- browserVersion={
- currentTest.testResult?.browserVersion?.name
- }
- browserVersions={testPlanReport.browser.browserVersions.map(
- item => item.name
- )}
- patternName={testPlanVersion.title}
- testerName={tester.username}
- exactAtVersion={testPlanReport.exactAtVersion}
- handleAction={handleAtAndBrowserDetailsModalAction}
- handleClose={handleAtAndBrowserDetailsModalCloseAction}
- />
- )}
-
- )
+ )}
+
+
+ {menuRightOfContent}
+
+
+ )}
+
+
+
+
+ {/* Modals */}
+ {showStartOverModal && (
+ setShowStartOverModal(false)}
+ />
+ )}
+ {showGetInvolvedModal && (
+
+ Only members of the ARIA-AT test team can submit data. If you
+ fill in this form, your data will not be saved! Check out the{' '}
+ home page to learn more about how to get
+ involved.
+ >
+ }
+ closeLabel="Close"
+ handleClose={() => setShowGetInvolvedModal(false)}
+ />
+ )}
+ {showReviewConflictsModal && (
+ setShowReviewConflictsModal(false)}
+ />
+ )}
+ >
+ );
+ };
+
+ let heading;
+ let content;
+
+ heading = pageReady && (
+
+ );
+
+ if (!isSignedIn || !testPlanRun?.isComplete) {
+ content = testCount ? (
+ renderTestContent(testPlanReport, currentTest, heading)
+ ) : (
+ // No tests loaded
+ <>
+ {heading}
+ No tests for this At and Browser combination
+ >
);
+ } else {
+ content = (
+
+ {heading}
+
+
+ Thanks! Your results have been
+ submitted. Please return to the{' '}
+ Test Queue.
+
+
+
+ );
+ }
+
+ return (
+ pageReady && (
+
+
+
+
+ {testCount
+ ? `${currentTest.title} for ${testPlanReport.at?.name} ${currentAtVersion?.name} and ${testPlanReport.browser?.name} ${currentBrowserVersion?.name} ` +
+ `| ARIA-AT`
+ : 'No tests for this AT and Browser | ARIA-AT'}
+
+
+ {updateMessageComponent && (
+
+ {updateMessageComponent}
+
+ )}
+
+
+
+
+ {content}
+
+
+
+ {showThemedModal && (
+
+ )}
+ {isSignedIn && isShowingAtBrowserModal && (
+ {
+ // Only provide at version options that released
+ // at the same time or later than the minimum
+ // AT version
+ let earliestReleasedAt = null;
+ if (testPlanReport.minimumAtVersion) {
+ earliestReleasedAt = new Date(
+ testPlanReport.minimumAtVersion.releasedAt
+ );
+ return new Date(item.releasedAt) >= earliestReleasedAt;
+ }
+ return item;
+ })
+ .map(item => item.name)}
+ exactAtVersion={testPlanReport.exactAtVersion}
+ browserName={testPlanReport.browser.name}
+ browserVersion={currentTest.testResult?.browserVersion?.name}
+ browserVersions={testPlanReport.browser.browserVersions.map(
+ item => item.name
+ )}
+ patternName={testPlanVersion.title}
+ testerName={tester.username}
+ handleAction={handleAtAndBrowserDetailsModalAction}
+ handleClose={handleAtAndBrowserDetailsModalCloseAction}
+ />
+ )}
+
+
+ )
+ );
};
export default TestRun;
diff --git a/client/components/TestRun/queries.js b/client/components/TestRun/queries.js
index aed6ca3e1..1499f6f87 100644
--- a/client/components/TestRun/queries.js
+++ b/client/components/TestRun/queries.js
@@ -1,1269 +1,1268 @@
import { gql } from '@apollo/client';
export const TEST_RUN_PAGE_QUERY = gql`
- query TestPlanRunPage($testPlanRunId: ID!) {
- testPlanRun(id: $testPlanRunId) {
+ query TestPlanRunPage($testPlanRunId: ID!) {
+ testPlanRun(id: $testPlanRunId) {
+ id
+ initiatedByAutomation
+ collectionJob {
+ id
+ status
+ externalLogsUrl
+ testStatus {
+ test {
id
- initiatedByAutomation
- tester {
- id
+ }
+ status
+ }
+ }
+ tester {
+ id
+ username
+ isBot
+ }
+ testResults {
+ id
+ startedAt
+ completedAt
+ test {
+ id
+ rowNumber
+ title
+ renderedUrl
+ renderableContent
+ }
+ scenarioResults {
+ id
+ scenario {
+ commands {
+ id
+ text
+ }
+ }
+ output
+ assertionResults {
+ id
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ mustAssertionResults: assertionResults(priority: MUST) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ shouldAssertionResults: assertionResults(priority: SHOULD) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ mayAssertionResults: assertionResults(priority: MAY) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ unexpectedBehaviors {
+ id
+ text
+ impact
+ details
+ }
+ }
+ atVersion {
+ id
+ name
+ }
+ browserVersion {
+ id
+ name
+ }
+ }
+ testPlanReport {
+ id
+ conflicts {
+ source {
+ test {
+ id
+ title
+ rowNumber
+ }
+ scenario {
+ id
+ commands {
+ text
+ }
+ }
+ assertion {
+ id
+ text
+ phrase
+ }
+ }
+ conflictingResults {
+ testPlanRun {
+ id
+ tester {
username
- isBot
+ }
+ }
+ scenarioResult {
+ output
+ unexpectedBehaviors {
+ text
+ impact
+ details
+ }
+ }
+ assertionResult {
+ passed
+ }
+ }
+ }
+ at {
+ id
+ name
+ atVersions {
+ id
+ name
+ releasedAt
+ }
+ }
+ minimumAtVersion {
+ id
+ name
+ releasedAt
+ }
+ exactAtVersion {
+ id
+ name
+ }
+ browser {
+ id
+ name
+ browserVersions {
+ id
+ name
+ }
+ }
+ testPlanVersion {
+ id
+ title
+ phase
+ updatedAt
+ gitSha
+ testPageUrl
+ testPlan {
+ directory
+ }
+ metadata
+ }
+ runnableTests {
+ id
+ rowNumber
+ title
+ ats {
+ id
+ name
+ }
+ renderedUrl
+ scenarios {
+ id
+ at {
+ id
+ name
+ }
+ commands {
+ id
+ text
+ }
+ }
+ assertions {
+ id
+ priority
+ text
+ }
+ }
+ }
+ }
+ me {
+ id
+ username
+ roles
+ }
+ users {
+ id
+ username
+ isBot
+ }
+ }
+`;
+
+export const COLLECTION_JOB_UPDATES_QUERY = gql`
+ query CollectionJob($collectionJobId: ID!) {
+ collectionJob(id: $collectionJobId) {
+ id
+ status
+ externalLogsUrl
+ testStatus {
+ test {
+ id
+ }
+ status
+ }
+ }
+ }
+`;
+
+export const TEST_RUN_PAGE_ANON_QUERY = gql`
+ query TestPlanRunAnonPage($testPlanReportId: ID!) {
+ testPlanReport(id: $testPlanReportId) {
+ id
+ conflicts {
+ source {
+ test {
+ id
+ title
+ rowNumber
+ }
+ scenario {
+ id
+ commands {
+ text
+ }
+ }
+ assertion {
+ id
+ text
+ phrase
+ }
+ }
+ conflictingResults {
+ testPlanRun {
+ id
+ tester {
+ username
+ isBot
+ }
+ }
+ scenarioResult {
+ output
+ unexpectedBehaviors {
+ text
+ impact
+ details
+ }
+ }
+ assertionResult {
+ passed
+ }
+ }
+ }
+ at {
+ id
+ name
+ atVersions {
+ id
+ name
+ }
+ }
+ browser {
+ id
+ name
+ browserVersions {
+ id
+ name
+ }
+ }
+ testPlanVersion {
+ id
+ title
+ phase
+ updatedAt
+ gitSha
+ testPageUrl
+ testPlan {
+ directory
+ }
+ metadata
+ }
+ runnableTests {
+ id
+ rowNumber
+ title
+ ats {
+ id
+ name
+ }
+ renderedUrl
+ renderableContent
+ scenarios {
+ id
+ at {
+ id
+ name
+ }
+ commands {
+ id
+ text
+ }
+ }
+ assertions {
+ id
+ priority
+ text
+ }
+ }
+ }
+ }
+`;
+
+export const FIND_OR_CREATE_TEST_RESULT_MUTATION = gql`
+ mutation FindOrCreateTestResult(
+ $testPlanRunId: ID!
+ $testId: ID!
+ $atVersionId: ID!
+ $browserVersionId: ID!
+ ) {
+ testPlanRun(id: $testPlanRunId) {
+ findOrCreateTestResult(
+ testId: $testId
+ atVersionId: $atVersionId
+ browserVersionId: $browserVersionId
+ ) {
+ locationOfData
+ testPlanRun {
+ id
+ tester {
+ id
+ username
+ isBot
+ }
+ testResults {
+ id
+ startedAt
+ completedAt
+ test {
+ id
+ rowNumber
+ title
+ renderedUrl
+ renderableContent
}
- testResults {
+ scenarioResults {
+ id
+ scenario {
+ commands {
+ id
+ text
+ }
+ }
+ output
+ assertionResults {
id
- startedAt
- completedAt
- test {
- id
- rowNumber
- title
- renderedUrl
- renderableContent
+ assertion {
+ text
+ phrase
}
- scenarioResults {
- id
- scenario {
- commands {
- id
- text
- }
- }
- output
- assertionResults {
- id
- assertion {
- text
- phrase
- }
- passed
- }
- mustAssertionResults: assertionResults(priority: MUST) {
- assertion {
- text
- phrase
- }
- passed
- }
- shouldAssertionResults: assertionResults(priority: SHOULD) {
- assertion {
- text
- phrase
- }
- passed
- }
- mayAssertionResults: assertionResults(priority: MAY) {
- assertion {
- text
- phrase
- }
- passed
- }
- unexpectedBehaviors {
- id
- text
- impact
- details
- }
+ passed
+ }
+ mustAssertionResults: assertionResults(priority: MUST) {
+ assertion {
+ text
+ phrase
}
- atVersion {
- id
- name
+ passed
+ }
+ shouldAssertionResults: assertionResults(priority: SHOULD) {
+ assertion {
+ text
+ phrase
}
- browserVersion {
- id
- name
+ passed
+ }
+ mayAssertionResults: assertionResults(priority: MAY) {
+ assertion {
+ text
+ phrase
}
- }
- testPlanReport {
+ passed
+ }
+ unexpectedBehaviors {
id
- conflicts {
- source {
- test {
- id
- title
- rowNumber
- }
- scenario {
- id
- commands {
- text
- }
- }
- assertion {
- id
- text
- phrase
- }
- }
- conflictingResults {
- testPlanRun {
- id
- tester {
- username
- }
- }
- scenarioResult {
- output
- unexpectedBehaviors {
- text
- impact
- details
- }
- }
- assertionResult {
- passed
- }
- }
+ text
+ impact
+ details
+ }
+ }
+ atVersion {
+ id
+ name
+ }
+ browserVersion {
+ id
+ name
+ }
+ }
+ testPlanReport {
+ id
+ conflicts {
+ source {
+ test {
+ id
+ title
+ rowNumber
}
- at {
- id
- name
- atVersions {
- id
- name
- releasedAt
- }
+ scenario {
+ id
+ commands {
+ text
+ }
}
- minimumAtVersion {
- id
- name
- releasedAt
+ assertion {
+ id
+ text
+ phrase
}
- exactAtVersion {
- id
- name
+ }
+ conflictingResults {
+ testPlanRun {
+ id
+ tester {
+ username
+ }
}
- browser {
- id
- name
- browserVersions {
- id
- name
- }
+ scenarioResult {
+ output
+ unexpectedBehaviors {
+ text
+ impact
+ details
+ }
}
- testPlanVersion {
- id
- title
- phase
- updatedAt
- gitSha
- testPageUrl
- testPlan {
- directory
- }
- metadata
+ assertionResult {
+ passed
}
- runnableTests {
- id
- rowNumber
- title
- ats {
- id
- name
- }
- renderedUrl
- scenarios {
- id
- at {
- id
- name
- }
- commands {
- id
- text
- }
- }
- assertions {
- id
- priority
- text
- }
+ }
+ }
+ at {
+ id
+ name
+ atVersions {
+ id
+ name
+ releasedAt
+ }
+ }
+ minimumAtVersion {
+ id
+ name
+ releasedAt
+ }
+ exactAtVersion {
+ id
+ name
+ }
+ browser {
+ id
+ name
+ browserVersions {
+ id
+ name
+ }
+ }
+ testPlanVersion {
+ id
+ title
+ phase
+ updatedAt
+ gitSha
+ testPageUrl
+ testPlan {
+ directory
+ }
+ metadata
+ }
+ runnableTests {
+ id
+ rowNumber
+ title
+ ats {
+ id
+ name
+ }
+ renderedUrl
+ scenarios {
+ id
+ at {
+ id
+ name
}
+ commands {
+ id
+ text
+ }
+ }
+ assertions {
+ id
+ priority
+ text
+ }
}
+ }
}
- me {
+ testPlanReport {
+ id
+ conflicts {
+ source {
+ test {
+ id
+ title
+ rowNumber
+ }
+ scenario {
+ id
+ commands {
+ text
+ }
+ }
+ assertion {
+ id
+ text
+ phrase
+ }
+ }
+ conflictingResults {
+ testPlanRun {
+ id
+ tester {
+ isBot
+ username
+ }
+ }
+ scenarioResult {
+ output
+ unexpectedBehaviors {
+ text
+ impact
+ details
+ }
+ }
+ assertionResult {
+ passed
+ }
+ }
+ }
+ at {
id
- username
- roles
- }
- users {
+ name
+ atVersions {
+ id
+ name
+ releasedAt
+ }
+ }
+ minimumAtVersion {
id
- username
- isBot
+ name
+ releasedAt
+ }
+ exactAtVersion {
+ id
+ name
+ }
+ browser {
+ id
+ name
+ browserVersions {
+ id
+ name
+ }
+ }
+ testPlanVersion {
+ id
+ title
+ phase
+ updatedAt
+ gitSha
+ testPageUrl
+ testPlan {
+ directory
+ }
+ metadata
+ }
+ runnableTests {
+ id
+ rowNumber
+ title
+ ats {
+ id
+ name
+ }
+ renderedUrl
+ scenarios {
+ id
+ at {
+ id
+ name
+ }
+ commands {
+ id
+ text
+ }
+ }
+ assertions {
+ id
+ priority
+ text
+ }
+ }
}
+ }
}
+ }
`;
-export const TEST_RUN_PAGE_ANON_QUERY = gql`
- query TestPlanRunAnonPage($testPlanReportId: ID!) {
- testPlanReport(id: $testPlanReportId) {
+export const SAVE_TEST_RESULT_MUTATION = gql`
+ mutation SaveTestResult(
+ $id: ID!
+ $atVersionId: ID!
+ $browserVersionId: ID!
+ $scenarioResults: [ScenarioResultInput]!
+ ) {
+ testResult(id: $id) {
+ saveTestResult(
+ input: {
+ id: $id
+ atVersionId: $atVersionId
+ browserVersionId: $browserVersionId
+ scenarioResults: $scenarioResults
+ }
+ ) {
+ locationOfData
+ testPlanRun {
+ id
+ tester {
+ id
+ username
+ isBot
+ }
+ testResults {
+ id
+ startedAt
+ completedAt
+ test {
+ id
+ rowNumber
+ title
+ renderedUrl
+ renderableContent
+ }
+ scenarioResults {
+ id
+ scenario {
+ commands {
+ id
+ text
+ }
+ }
+ output
+ assertionResults {
+ id
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ mustAssertionResults: assertionResults(priority: MUST) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ shouldAssertionResults: assertionResults(priority: SHOULD) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ mayAssertionResults: assertionResults(priority: MAY) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ unexpectedBehaviors {
+ id
+ text
+ impact
+ details
+ }
+ }
+ atVersion {
+ id
+ name
+ }
+ browserVersion {
+ id
+ name
+ }
+ }
+ testPlanReport {
id
conflicts {
- source {
- test {
- id
- title
- rowNumber
- }
- scenario {
- id
- commands {
- text
- }
- }
- assertion {
- id
- text
- phrase
- }
+ source {
+ test {
+ id
+ title
+ rowNumber
+ }
+ scenario {
+ id
+ commands {
+ text
+ }
}
- conflictingResults {
- testPlanRun {
- id
- tester {
- username
- isBot
- }
- }
- scenarioResult {
- output
- unexpectedBehaviors {
- text
- impact
- details
- }
- }
- assertionResult {
- passed
- }
+ assertion {
+ id
+ text
+ phrase
+ }
+ }
+ conflictingResults {
+ testPlanRun {
+ id
+ tester {
+ username
+ }
}
+ scenarioResult {
+ output
+ unexpectedBehaviors {
+ text
+ impact
+ details
+ }
+ }
+ assertionResult {
+ passed
+ }
+ }
}
at {
+ id
+ name
+ atVersions {
id
name
- atVersions {
- id
- name
- }
+ releasedAt
+ }
+ }
+ minimumAtVersion {
+ id
+ name
+ releasedAt
+ }
+ exactAtVersion {
+ id
+ name
}
browser {
+ id
+ name
+ browserVersions {
id
name
- browserVersions {
- id
- name
- }
+ }
}
testPlanVersion {
+ id
+ title
+ phase
+ updatedAt
+ gitSha
+ testPageUrl
+ testPlan {
+ directory
+ }
+ metadata
+ }
+ runnableTests {
+ id
+ rowNumber
+ title
+ ats {
id
- title
- phase
- updatedAt
- gitSha
- testPageUrl
- testPlan {
- directory
+ name
+ }
+ renderedUrl
+ scenarios {
+ id
+ at {
+ id
+ name
}
- metadata
+ commands {
+ id
+ text
+ }
+ }
+ assertions {
+ id
+ priority
+ text
+ }
}
- runnableTests {
+ }
+ }
+ testPlanReport {
+ id
+ conflicts {
+ source {
+ test {
id
- rowNumber
title
- ats {
- id
- name
+ rowNumber
+ }
+ scenario {
+ id
+ commands {
+ text
}
- renderedUrl
- renderableContent
- scenarios {
- id
- at {
- id
- name
- }
- commands {
- id
- text
- }
+ }
+ assertion {
+ id
+ text
+ phrase
+ }
+ }
+ conflictingResults {
+ testPlanRun {
+ id
+ tester {
+ username
}
- assertions {
- id
- priority
- text
+ }
+ scenarioResult {
+ output
+ unexpectedBehaviors {
+ text
+ impact
+ details
}
+ }
+ assertionResult {
+ passed
+ }
+ }
+ }
+ at {
+ id
+ name
+ atVersions {
+ id
+ name
+ releasedAt
+ }
+ }
+ minimumAtVersion {
+ id
+ name
+ releasedAt
+ }
+ exactAtVersion {
+ id
+ name
+ }
+ browser {
+ id
+ name
+ browserVersions {
+ id
+ name
+ }
+ }
+ testPlanVersion {
+ id
+ title
+ phase
+ updatedAt
+ gitSha
+ testPageUrl
+ testPlan {
+ directory
+ }
+ metadata
+ }
+ runnableTests {
+ id
+ rowNumber
+ title
+ ats {
+ id
+ name
+ }
+ renderedUrl
+ scenarios {
+ id
+ at {
+ id
+ name
+ }
+ commands {
+ id
+ text
+ }
+ }
+ assertions {
+ id
+ priority
+ text
}
+ }
}
+ }
}
+ }
`;
-export const FIND_OR_CREATE_TEST_RESULT_MUTATION = gql`
- mutation FindOrCreateTestResult(
- $testPlanRunId: ID!
- $testId: ID!
- $atVersionId: ID!
- $browserVersionId: ID!
- ) {
- testPlanRun(id: $testPlanRunId) {
- findOrCreateTestResult(
- testId: $testId
- atVersionId: $atVersionId
- browserVersionId: $browserVersionId
- ) {
- locationOfData
- testPlanRun {
- id
- tester {
- id
- username
- isBot
- }
- testResults {
- id
- startedAt
- completedAt
- test {
- id
- rowNumber
- title
- renderedUrl
- renderableContent
- }
- scenarioResults {
- id
- scenario {
- commands {
- id
- text
- }
- }
- output
- assertionResults {
- id
- assertion {
- text
- phrase
- }
- passed
- }
- mustAssertionResults: assertionResults(
- priority: MUST
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- shouldAssertionResults: assertionResults(
- priority: SHOULD
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- mayAssertionResults: assertionResults(
- priority: MAY
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- unexpectedBehaviors {
- id
- text
- impact
- details
- }
- }
- atVersion {
- id
- name
- }
- browserVersion {
- id
- name
- }
- }
- testPlanReport {
- id
- conflicts {
- source {
- test {
- id
- title
- rowNumber
- }
- scenario {
- id
- commands {
- text
- }
- }
- assertion {
- id
- text
- phrase
- }
- }
- conflictingResults {
- testPlanRun {
- id
- tester {
- username
- }
- }
- scenarioResult {
- output
- unexpectedBehaviors {
- text
- impact
- details
- }
- }
- assertionResult {
- passed
- }
- }
- }
- at {
- id
- name
- atVersions {
- id
- name
- releasedAt
- }
- }
- minimumAtVersion {
- id
- name
- releasedAt
- }
- exactAtVersion {
- id
- name
- }
- browser {
- id
- name
- browserVersions {
- id
- name
- }
- }
- testPlanVersion {
- id
- title
- phase
- updatedAt
- gitSha
- testPageUrl
- testPlan {
- directory
- }
- metadata
- }
- runnableTests {
- id
- rowNumber
- title
- ats {
- id
- name
- }
- renderedUrl
- scenarios {
- id
- at {
- id
- name
- }
- commands {
- id
- text
- }
- }
- assertions {
- id
- priority
- text
- }
- }
- }
+export const SUBMIT_TEST_RESULT_MUTATION = gql`
+ mutation SubmitTestResult(
+ $id: ID!
+ $atVersionId: ID!
+ $browserVersionId: ID!
+ $scenarioResults: [ScenarioResultInput]!
+ ) {
+ testResult(id: $id) {
+ submitTestResult(
+ input: {
+ id: $id
+ atVersionId: $atVersionId
+ browserVersionId: $browserVersionId
+ scenarioResults: $scenarioResults
+ }
+ ) {
+ locationOfData
+ testPlanRun {
+ id
+ tester {
+ id
+ username
+ }
+ testResults {
+ id
+ startedAt
+ completedAt
+ test {
+ id
+ rowNumber
+ title
+ renderedUrl
+ renderableContent
+ }
+ scenarioResults {
+ id
+ scenario {
+ commands {
+ id
+ text
}
- testPlanReport {
- id
- conflicts {
- source {
- test {
- id
- title
- rowNumber
- }
- scenario {
- id
- commands {
- text
- }
- }
- assertion {
- id
- text
- phrase
- }
- }
- conflictingResults {
- testPlanRun {
- id
- tester {
- isBot
- username
- }
- }
- scenarioResult {
- output
- unexpectedBehaviors {
- text
- impact
- details
- }
- }
- assertionResult {
- passed
- }
- }
- }
- at {
- id
- name
- atVersions {
- id
- name
- releasedAt
- }
- }
- minimumAtVersion {
- id
- name
- releasedAt
- }
- exactAtVersion {
- id
- name
- }
- browser {
- id
- name
- browserVersions {
- id
- name
- }
- }
- testPlanVersion {
- id
- title
- phase
- updatedAt
- gitSha
- testPageUrl
- testPlan {
- directory
- }
- metadata
- }
- runnableTests {
- id
- rowNumber
- title
- ats {
- id
- name
- }
- renderedUrl
- scenarios {
- id
- at {
- id
- name
- }
- commands {
- id
- text
- }
- }
- assertions {
- id
- priority
- text
- }
- }
+ }
+ output
+ assertionResults {
+ id
+ assertion {
+ text
+ phrase
}
+ passed
+ }
+ mustAssertionResults: assertionResults(priority: MUST) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ shouldAssertionResults: assertionResults(priority: SHOULD) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ mayAssertionResults: assertionResults(priority: MAY) {
+ assertion {
+ text
+ phrase
+ }
+ passed
+ }
+ unexpectedBehaviors {
+ id
+ text
+ impact
+ details
+ }
}
- }
- }
-`;
-
-export const SAVE_TEST_RESULT_MUTATION = gql`
- mutation SaveTestResult(
- $id: ID!
- $atVersionId: ID!
- $browserVersionId: ID!
- $scenarioResults: [ScenarioResultInput]!
- ) {
- testResult(id: $id) {
- saveTestResult(
- input: {
- id: $id
- atVersionId: $atVersionId
- browserVersionId: $browserVersionId
- scenarioResults: $scenarioResults
+ atVersion {
+ id
+ name
+ }
+ browserVersion {
+ id
+ name
+ }
+ }
+ testPlanReport {
+ id
+ conflicts {
+ source {
+ test {
+ id
+ title
+ rowNumber
+ }
+ scenario {
+ id
+ commands {
+ text
+ }
+ }
+ assertion {
+ id
+ text
+ phrase
}
- ) {
- locationOfData
+ }
+ conflictingResults {
testPlanRun {
- id
- tester {
- id
- username
- isBot
- }
- testResults {
- id
- startedAt
- completedAt
- test {
- id
- rowNumber
- title
- renderedUrl
- renderableContent
- }
- scenarioResults {
- id
- scenario {
- commands {
- id
- text
- }
- }
- output
- assertionResults {
- id
- assertion {
- text
- phrase
- }
- passed
- }
- mustAssertionResults: assertionResults(
- priority: MUST
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- shouldAssertionResults: assertionResults(
- priority: SHOULD
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- mayAssertionResults: assertionResults(
- priority: MAY
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- unexpectedBehaviors {
- id
- text
- impact
- details
- }
- }
- atVersion {
- id
- name
- }
- browserVersion {
- id
- name
- }
- }
- testPlanReport {
- id
- conflicts {
- source {
- test {
- id
- title
- rowNumber
- }
- scenario {
- id
- commands {
- text
- }
- }
- assertion {
- id
- text
- phrase
- }
- }
- conflictingResults {
- testPlanRun {
- id
- tester {
- username
- }
- }
- scenarioResult {
- output
- unexpectedBehaviors {
- text
- impact
- details
- }
- }
- assertionResult {
- passed
- }
- }
- }
- at {
- id
- name
- atVersions {
- id
- name
- releasedAt
- }
- }
- minimumAtVersion {
- id
- name
- releasedAt
- }
- exactAtVersion {
- id
- name
- }
- browser {
- id
- name
- browserVersions {
- id
- name
- }
- }
- testPlanVersion {
- id
- title
- phase
- updatedAt
- gitSha
- testPageUrl
- testPlan {
- directory
- }
- metadata
- }
- runnableTests {
- id
- rowNumber
- title
- ats {
- id
- name
- }
- renderedUrl
- scenarios {
- id
- at {
- id
- name
- }
- commands {
- id
- text
- }
- }
- assertions {
- id
- priority
- text
- }
- }
- }
+ id
+ tester {
+ username
+ }
}
- testPlanReport {
- id
- conflicts {
- source {
- test {
- id
- title
- rowNumber
- }
- scenario {
- id
- commands {
- text
- }
- }
- assertion {
- id
- text
- phrase
- }
- }
- conflictingResults {
- testPlanRun {
- id
- tester {
- username
- }
- }
- scenarioResult {
- output
- unexpectedBehaviors {
- text
- impact
- details
- }
- }
- assertionResult {
- passed
- }
- }
- }
- at {
- id
- name
- atVersions {
- id
- name
- releasedAt
- }
- }
- minimumAtVersion {
- id
- name
- releasedAt
- }
- exactAtVersion {
- id
- name
- }
- browser {
- id
- name
- browserVersions {
- id
- name
- }
- }
- testPlanVersion {
- id
- title
- phase
- updatedAt
- gitSha
- testPageUrl
- testPlan {
- directory
- }
- metadata
- }
- runnableTests {
- id
- rowNumber
- title
- ats {
- id
- name
- }
- renderedUrl
- scenarios {
- id
- at {
- id
- name
- }
- commands {
- id
- text
- }
- }
- assertions {
- id
- priority
- text
- }
- }
+ scenarioResult {
+ output
+ unexpectedBehaviors {
+ text
+ impact
+ details
+ }
+ }
+ assertionResult {
+ passed
}
+ }
}
+ at {
+ id
+ name
+ atVersions {
+ id
+ name
+ releasedAt
+ }
+ }
+ minimumAtVersion {
+ id
+ name
+ releasedAt
+ }
+ exactAtVersion {
+ id
+ name
+ }
+ browser {
+ id
+ name
+ browserVersions {
+ id
+ name
+ }
+ }
+ testPlanVersion {
+ id
+ title
+ phase
+ updatedAt
+ gitSha
+ testPageUrl
+ testPlan {
+ directory
+ }
+ metadata
+ }
+ runnableTests {
+ id
+ rowNumber
+ title
+ ats {
+ id
+ name
+ }
+ renderedUrl
+ scenarios {
+ id
+ at {
+ id
+ name
+ }
+ commands {
+ id
+ text
+ }
+ }
+ assertions {
+ id
+ priority
+ text
+ }
+ }
+ }
}
- }
-`;
-
-export const SUBMIT_TEST_RESULT_MUTATION = gql`
- mutation SubmitTestResult(
- $id: ID!
- $atVersionId: ID!
- $browserVersionId: ID!
- $scenarioResults: [ScenarioResultInput]!
- ) {
- testResult(id: $id) {
- submitTestResult(
- input: {
- id: $id
- atVersionId: $atVersionId
- browserVersionId: $browserVersionId
- scenarioResults: $scenarioResults
+ testPlanReport {
+ id
+ conflicts {
+ source {
+ test {
+ id
+ title
+ rowNumber
+ }
+ scenario {
+ id
+ commands {
+ text
}
- ) {
- locationOfData
- testPlanRun {
- id
- tester {
- id
- username
- }
- testResults {
- id
- startedAt
- completedAt
- test {
- id
- rowNumber
- title
- renderedUrl
- renderableContent
- }
- scenarioResults {
- id
- scenario {
- commands {
- id
- text
- }
- }
- output
- assertionResults {
- id
- assertion {
- text
- phrase
- }
- passed
- }
- mustAssertionResults: assertionResults(
- priority: MUST
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- shouldAssertionResults: assertionResults(
- priority: SHOULD
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- mayAssertionResults: assertionResults(
- priority: MAY
- ) {
- assertion {
- text
- phrase
- }
- passed
- }
- unexpectedBehaviors {
- id
- text
- impact
- details
- }
- }
- atVersion {
- id
- name
- }
- browserVersion {
- id
- name
- }
- }
- testPlanReport {
- id
- conflicts {
- source {
- test {
- id
- title
- rowNumber
- }
- scenario {
- id
- commands {
- text
- }
- }
- assertion {
- id
- text
- phrase
- }
- }
- conflictingResults {
- testPlanRun {
- id
- tester {
- username
- }
- }
- scenarioResult {
- output
- unexpectedBehaviors {
- text
- impact
- details
- }
- }
- assertionResult {
- passed
- }
- }
- }
- at {
- id
- name
- atVersions {
- id
- name
- releasedAt
- }
- }
- minimumAtVersion {
- id
- name
- releasedAt
- }
- exactAtVersion {
- id
- name
- }
- browser {
- id
- name
- browserVersions {
- id
- name
- }
- }
- testPlanVersion {
- id
- title
- phase
- updatedAt
- gitSha
- testPageUrl
- testPlan {
- directory
- }
- metadata
- }
- runnableTests {
- id
- rowNumber
- title
- ats {
- id
- name
- }
- renderedUrl
- scenarios {
- id
- at {
- id
- name
- }
- commands {
- id
- text
- }
- }
- assertions {
- id
- priority
- text
- }
- }
- }
+ }
+ assertion {
+ id
+ text
+ phrase
+ }
+ }
+ conflictingResults {
+ testPlanRun {
+ id
+ tester {
+ username
}
- testPlanReport {
- id
- conflicts {
- source {
- test {
- id
- title
- rowNumber
- }
- scenario {
- id
- commands {
- text
- }
- }
- assertion {
- id
- text
- phrase
- }
- }
- conflictingResults {
- testPlanRun {
- id
- tester {
- username
- }
- }
- scenarioResult {
- output
- unexpectedBehaviors {
- text
- impact
- details
- }
- }
- assertionResult {
- passed
- }
- }
- }
- at {
- id
- name
- atVersions {
- id
- name
- releasedAt
- }
- }
- minimumAtVersion {
- id
- name
- releasedAt
- }
- exactAtVersion {
- id
- name
- }
- browser {
- id
- name
- browserVersions {
- id
- name
- }
- }
- testPlanVersion {
- id
- title
- phase
- updatedAt
- gitSha
- testPageUrl
- testPlan {
- directory
- }
- metadata
- }
- runnableTests {
- id
- rowNumber
- title
- ats {
- id
- name
- }
- renderedUrl
- scenarios {
- id
- at {
- id
- name
- }
- commands {
- id
- text
- }
- }
- assertions {
- id
- priority
- text
- }
- }
+ }
+ scenarioResult {
+ output
+ unexpectedBehaviors {
+ text
+ impact
+ details
}
+ }
+ assertionResult {
+ passed
+ }
}
+ }
+ at {
+ id
+ name
+ atVersions {
+ id
+ name
+ releasedAt
+ }
+ }
+ minimumAtVersion {
+ id
+ name
+ releasedAt
+ }
+ exactAtVersion {
+ id
+ name
+ }
+ browser {
+ id
+ name
+ browserVersions {
+ id
+ name
+ }
+ }
+ testPlanVersion {
+ id
+ title
+ phase
+ updatedAt
+ gitSha
+ testPageUrl
+ testPlan {
+ directory
+ }
+ metadata
+ }
+ runnableTests {
+ id
+ rowNumber
+ title
+ ats {
+ id
+ name
+ }
+ renderedUrl
+ scenarios {
+ id
+ at {
+ id
+ name
+ }
+ commands {
+ id
+ text
+ }
+ }
+ assertions {
+ id
+ priority
+ text
+ }
+ }
}
+ }
}
+ }
`;
export const DELETE_TEST_RESULT_MUTATION = gql`
- mutation DeleteTestResult($id: ID!) {
- testResult(id: $id) {
- deleteTestResult {
- locationOfData
- }
- }
+ mutation DeleteTestResult($id: ID!) {
+ testResult(id: $id) {
+ deleteTestResult {
+ locationOfData
+ }
}
+ }
`;
export const FIND_OR_CREATE_BROWSER_VERSION_MUTATION = gql`
- mutation FindOrCreateBrowserVersion(
- $browserId: ID!
- $browserVersionName: String!
- ) {
- browser(id: $browserId) {
- findOrCreateBrowserVersion(input: { name: $browserVersionName }) {
- id
- name
- }
- }
- }
-`;
-
-export const COLLECTION_JOB_STATUS_BY_TEST_PLAN_RUN_ID_QUERY = gql`
- query CollectionJobIdByTestPlanRunId($testPlanRunId: ID!) {
- collectionJobByTestPlanRunId(testPlanRunId: $testPlanRunId) {
- id
- status
- externalLogsUrl
- }
+ mutation FindOrCreateBrowserVersion(
+ $browserId: ID!
+ $browserVersionName: String!
+ ) {
+ browser(id: $browserId) {
+ findOrCreateBrowserVersion(input: { name: $browserVersionName }) {
+ id
+ name
+ }
}
+ }
`;
diff --git a/client/components/UserSettings/UserSettings.jsx b/client/components/UserSettings/UserSettings.jsx
index f28e34a60..3a8f99d22 100644
--- a/client/components/UserSettings/UserSettings.jsx
+++ b/client/components/UserSettings/UserSettings.jsx
@@ -6,151 +6,137 @@ import PageStatus from '../common/PageStatus';
import { CURRENT_SETTINGS_QUERY, UPDATE_ME_MUTATION } from './queries';
const UserSettings = () => {
- const { loading, data, error } = useQuery(CURRENT_SETTINGS_QUERY);
+ const { loading, data, error } = useQuery(CURRENT_SETTINGS_QUERY);
- const [updateMe] = useMutation(UPDATE_ME_MUTATION, {
- refetchQueries: [{ query: CURRENT_SETTINGS_QUERY }]
- });
+ const [updateMe] = useMutation(UPDATE_ME_MUTATION, {
+ refetchQueries: [{ query: CURRENT_SETTINGS_QUERY }]
+ });
- const [checkedAts, setCheckedAts] = useState(undefined);
+ const [checkedAts, setCheckedAts] = useState(undefined);
- const handleCheckedAt = useCallback(
- event => {
- const atId = event.target.id;
- const isChecked = checkedAts.includes(atId);
- if (isChecked) {
- setCheckedAts(checkedAts.filter(item => item !== atId));
- } else {
- setCheckedAts([...checkedAts, atId]);
- }
- },
- [checkedAts]
- );
+ const handleCheckedAt = useCallback(
+ event => {
+ const atId = event.target.id;
+ const isChecked = checkedAts.includes(atId);
+ if (isChecked) {
+ setCheckedAts(checkedAts.filter(item => item !== atId));
+ } else {
+ setCheckedAts([...checkedAts, atId]);
+ }
+ },
+ [checkedAts]
+ );
- const handleSave = useCallback(
- event => {
- event.preventDefault();
- updateMe({ variables: { input: { atIds: checkedAts } } });
- },
- [checkedAts]
- );
+ const handleSave = useCallback(
+ event => {
+ event.preventDefault();
+ updateMe({ variables: { input: { atIds: checkedAts } } });
+ },
+ [checkedAts]
+ );
- useEffect(() => {
- if (!data) return;
- setCheckedAts(data.me.ats.map(at => at.id));
- }, [data]);
+ useEffect(() => {
+ if (!data) return;
+ setCheckedAts(data.me.ats.map(at => at.id));
+ }, [data]);
- if (error) {
- return (
-
- );
- }
+ if (error) {
+ return (
+
+ );
+ }
- if (loading) {
- return (
-
- );
- }
+ if (loading) {
+ return (
+
+ );
+ }
- if (!data || !checkedAts) return null;
+ if (!data || !checkedAts) return null;
- const {
- ats,
- me: { username, ats: userAtsData }
- } = data;
- const savedAts = userAtsData.map(at => at.id);
+ const {
+ ats,
+ me: { username, ats: userAtsData }
+ } = data;
+ const savedAts = userAtsData.map(at => at.id);
- return (
-
-
-
- Settings | ARIA-AT
-
- Settings
-
- User Details
-
-
- {username}
-
-
- Assistive Technology Settings
-
- {savedAts.length > 0 ? (
-
-
- You can currently test the following
- assistive technologies:
-
-
- {ats
- .filter(({ id: atId }) =>
- savedAts.includes(atId)
- )
- .map(at => (
- {at.name}
- ))}
-
-
- ) : (
-
- You currently are not configured to run any
- assistive technologies.
-
- )}
-
-
- Submit the form below to update the assistive
- technologies you can test:
-
-
- {ats &&
- ats.map(at => {
- return (
- atId === at.id
- )
- }
- />
- );
- })}
-
-
- Save
-
-
-
-
-
- );
+ return (
+
+
+
+ Settings | ARIA-AT
+
+ Settings
+
+ User Details
+
+
+ {username}
+
+
+ Assistive Technology Settings
+
+ {savedAts.length > 0 ? (
+
+
+ You can currently test the following assistive technologies:
+
+
+ {ats
+ .filter(({ id: atId }) => savedAts.includes(atId))
+ .map(at => (
+ {at.name}
+ ))}
+
+
+ ) : (
+
+ You currently are not configured to run any assistive
+ technologies.
+
+ )}
+
+
+ Submit the form below to update the assistive technologies you can
+ test:
+
+
+ {ats &&
+ ats.map(at => {
+ return (
+ atId === at.id)}
+ />
+ );
+ })}
+
+
+ Save
+
+
+
+
+
+ );
};
export default UserSettings;
diff --git a/client/components/UserSettings/queries.js b/client/components/UserSettings/queries.js
index a04a00da9..552a47c2d 100644
--- a/client/components/UserSettings/queries.js
+++ b/client/components/UserSettings/queries.js
@@ -1,27 +1,27 @@
import { gql } from '@apollo/client';
export const CURRENT_SETTINGS_QUERY = gql`
- query CurrentSettings {
- me {
- id
- username
- ats {
- id
- }
- }
- ats {
- id
- name
- }
+ query CurrentSettings {
+ me {
+ id
+ username
+ ats {
+ id
+ }
}
+ ats {
+ id
+ name
+ }
+ }
`;
export const UPDATE_ME_MUTATION = gql`
- mutation UpdateMe($input: UserInput) {
- updateMe(input: $input) {
- ats {
- id
- }
- }
+ mutation UpdateMe($input: UserInput) {
+ updateMe(input: $input) {
+ ats {
+ id
+ }
}
+ }
`;
diff --git a/client/components/common/AssignTesterDropdown/AssignTesterDropdown.css b/client/components/common/AssignTesterDropdown/AssignTesterDropdown.css
new file mode 100644
index 000000000..e1ba49d3d
--- /dev/null
+++ b/client/components/common/AssignTesterDropdown/AssignTesterDropdown.css
@@ -0,0 +1,34 @@
+.assignees {
+ margin-top: 0.3em;
+ font-size: 0.9rem;
+ text-align: center;
+}
+
+.assign-tester {
+ border-radius: 25px;
+ height: 2.5em;
+ border-style: dashed;
+ padding: 0.4em 0.6em;
+}
+
+.assign-tester .fa-user-plus {
+ margin: 0;
+}
+
+.assign-tester.dropdown-toggle:after {
+ display: none;
+}
+
+.no-assignees {
+ text-align: center;
+ line-height: 2;
+}
+
+.not-assigned.human {
+ padding-left: 1.5em;
+}
+
+[role='menu'].assign-menu {
+ max-height: 200px;
+ overflow-y: scroll;
+}
diff --git a/client/components/common/AssignTesterDropdown/index.jsx b/client/components/common/AssignTesterDropdown/index.jsx
new file mode 100644
index 000000000..f576549fd
--- /dev/null
+++ b/client/components/common/AssignTesterDropdown/index.jsx
@@ -0,0 +1,233 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Dropdown } from 'react-bootstrap';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import {
+ faCheck,
+ faChevronDown,
+ faRobot,
+ faUserPlus
+} from '@fortawesome/free-solid-svg-icons';
+import {
+ ASSIGN_TESTER_MUTATION,
+ REMOVE_TESTER_MUTATION,
+ TEST_PLAN_REPORT_AT_BROWSER_QUERY
+} from './queries';
+import { useMutation, useQuery } from '@apollo/client';
+import { LoadingStatus, useTriggerLoad } from '../LoadingStatus';
+import { SCHEDULE_COLLECTION_JOB_MUTATION } from '../../AddTestToQueueWithConfirmation/queries';
+import { isSupportedByResponseCollector } from '../../../utils/automation';
+
+import './AssignTesterDropdown.css';
+
+const AssignTesterDropdown = ({
+ testPlanReportId,
+ testPlanRun,
+ draftTestPlanRuns,
+ possibleTesters,
+ onChange,
+ label,
+ dropdownAssignTesterButtonRef,
+ setAlertMessage = () => {}
+}) => {
+ const { triggerLoad, loadingMessage } = useTriggerLoad();
+
+ const { data: testPlanReportAtBrowserQuery } = useQuery(
+ TEST_PLAN_REPORT_AT_BROWSER_QUERY,
+ {
+ variables: {
+ testPlanReportId: testPlanReportId
+ },
+ fetchPolicy: 'cache-and-network'
+ }
+ );
+
+ const [removeTester] = useMutation(REMOVE_TESTER_MUTATION);
+ const [assignTester] = useMutation(ASSIGN_TESTER_MUTATION);
+ const [scheduleCollection] = useMutation(SCHEDULE_COLLECTION_JOB_MUTATION);
+
+ const isTesterAssigned = username => {
+ if (testPlanRun) {
+ return testPlanRun.tester?.username === username;
+ } else if (draftTestPlanRuns?.length) {
+ return draftTestPlanRuns.some(
+ testPlanRun => testPlanRun.tester?.username === username
+ );
+ } else {
+ return false;
+ }
+ };
+
+ const toggleTesterAssign = async username => {
+ const testerIsAssigned = isTesterAssigned(username);
+ const tester = possibleTesters.find(tester => tester.username === username);
+
+ if (testerIsAssigned) {
+ await triggerLoad(async () => {
+ await removeTester({
+ variables: {
+ testReportId: testPlanReportId,
+ testerId: tester.id
+ }
+ });
+ }, `Updating Test Plan Assignees. Deleting Test Plan Run for ${tester.username}`);
+ } else {
+ if (tester.isBot) {
+ await triggerLoad(async () => {
+ await scheduleCollection({
+ variables: {
+ testPlanReportId: testPlanReportId
+ }
+ });
+ }, 'Scheduling Collection Job');
+ } else {
+ await triggerLoad(async () => {
+ await assignTester({
+ variables: {
+ testReportId: testPlanReportId,
+ testerId: tester.id,
+ testPlanRunId: testPlanRun?.id // if we are assigning to an existing test plan run, pass the id
+ }
+ });
+ }, 'Updating Test Plan Assignees');
+ }
+ }
+ };
+
+ const renderLabel = () => {
+ if (label) {
+ return (
+
+ {label}
+
+ );
+ } else {
+ return ;
+ }
+ };
+ const clearAriaLiveRegion = () => {
+ setAlertMessage('');
+ };
+
+ const handleKeyDown = event => {
+ const { key } = event;
+ if (key.match(/[0-9a-zA-Z]/)) {
+ const container = event.target.closest('[role=menu]');
+ const matchingMenuItem = Array.from(container.children).find(menuItem => {
+ return menuItem.innerText
+ .trim()
+ .toLowerCase()
+ .startsWith(key.toLowerCase());
+ });
+
+ if (matchingMenuItem) {
+ matchingMenuItem.focus();
+ }
+ }
+ };
+
+ return (
+
+
+
+ {renderLabel()}
+
+
+ {possibleTesters?.length ? (
+ possibleTesters.map(tester => {
+ const { username, isBot, ats } = tester;
+ const testerIsAssigned = isTesterAssigned(username);
+ const classname = [
+ testerIsAssigned ? 'assigned' : 'not-assigned',
+ isBot ? 'bot' : 'human'
+ ].join(' ');
+ let icon;
+ if (testerIsAssigned) {
+ icon = faCheck;
+ } else if (isBot) {
+ // if our bot doesn't have a link to the at - hide it from the list
+ if (
+ !ats.find(
+ ({ id }) =>
+ id === testPlanReportAtBrowserQuery?.testPlanReport.at.id
+ )
+ ) {
+ return null;
+ }
+
+ const supportedByBot = isSupportedByResponseCollector(
+ testPlanReportAtBrowserQuery?.testPlanReport
+ );
+ if (!supportedByBot) {
+ return null;
+ }
+ icon = faRobot;
+ }
+ return (
+ {
+ const updatedIsAssigned = !testerIsAssigned;
+ setAlertMessage(
+ `${username} ${
+ updatedIsAssigned
+ ? 'now checked'
+ : `now unchecked. ${tester.username}'s test plan run has been deleted.`
+ }`
+ );
+ setTimeout(clearAriaLiveRegion, 6000);
+ await toggleTesterAssign(username);
+ await onChange();
+ }}
+ >
+ {icon && }
+ {`${tester.username}`}
+
+ );
+ })
+ ) : (
+ No testers to assign
+ )}
+
+
+
+ );
+};
+
+AssignTesterDropdown.propTypes = {
+ testPlanReportId: PropTypes.string.isRequired,
+ possibleTesters: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ username: PropTypes.string.isRequired,
+ isBot: PropTypes.bool.isRequired,
+ ats: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired
+ })
+ )
+ })
+ ).isRequired,
+ onChange: PropTypes.func.isRequired,
+ testPlanRun: PropTypes.object,
+ label: PropTypes.string,
+ draftTestPlanRuns: PropTypes.array,
+ setAlertMessage: PropTypes.func,
+ dropdownAssignTesterButtonRef: PropTypes.object
+};
+
+export default AssignTesterDropdown;
diff --git a/client/components/common/AssignTesterDropdown/queries.js b/client/components/common/AssignTesterDropdown/queries.js
new file mode 100644
index 000000000..557cb9aeb
--- /dev/null
+++ b/client/components/common/AssignTesterDropdown/queries.js
@@ -0,0 +1,60 @@
+import { gql } from '@apollo/client';
+
+export const ASSIGN_TESTER_MUTATION = gql`
+ mutation AssignTester(
+ $testReportId: ID!
+ $testerId: ID!
+ $testPlanRunId: ID
+ ) {
+ testPlanReport(id: $testReportId) {
+ assignTester(userId: $testerId, testPlanRunId: $testPlanRunId) {
+ testPlanReport {
+ draftTestPlanRuns {
+ initiatedByAutomation
+ tester {
+ id
+ username
+ isBot
+ }
+ }
+ }
+ }
+ }
+ }
+`;
+
+export const REMOVE_TESTER_MUTATION = gql`
+ mutation RemoveTester($testReportId: ID!, $testerId: ID!) {
+ testPlanReport(id: $testReportId) {
+ deleteTestPlanRun(userId: $testerId) {
+ testPlanReport {
+ draftTestPlanRuns {
+ tester {
+ id
+ username
+ isBot
+ }
+ }
+ }
+ }
+ }
+ }
+`;
+
+export const TEST_PLAN_REPORT_AT_BROWSER_QUERY = gql`
+ query TestPlanReportAtBrowser($testPlanReportId: ID!) {
+ testPlanReport(id: $testPlanReportId) {
+ id
+ at {
+ id
+ key
+ name
+ }
+ browser {
+ id
+ key
+ name
+ }
+ }
+ }
+`;
diff --git a/client/components/common/AtAndBrowserDetailsModal/index.jsx b/client/components/common/AtAndBrowserDetailsModal/index.jsx
index 11dd21c25..962034a37 100644
--- a/client/components/common/AtAndBrowserDetailsModal/index.jsx
+++ b/client/components/common/AtAndBrowserDetailsModal/index.jsx
@@ -5,685 +5,601 @@ import { Form, Alert } from 'react-bootstrap';
import styled from '@emotion/styled';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
- faInfoCircle,
- faExclamationTriangle
+ faInfoCircle,
+ faExclamationTriangle
} from '@fortawesome/free-solid-svg-icons';
import { useDetectUa } from '../../../hooks/useDetectUa';
import BasicModal from '../BasicModal';
const ModalInnerSectionContainer = styled.div`
- display: flex;
- flex-direction: column;
+ display: flex;
+ flex-direction: column;
`;
const FieldsetRow = styled.fieldset`
- display: grid;
- grid-template-columns: 1fr 1fr;
- grid-gap: 1rem;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: 1rem;
- legend {
- float: inherit;
- }
+ legend {
+ float: inherit;
+ }
`;
const ModalSubtitleStyle = styled.h2`
- font-size: 0.8em;
- margin: 0;
- padding: 0;
+ font-size: 0.8em;
+ margin: 0;
+ padding: 0;
`;
const Required = styled.span`
- color: #ce1b4c;
+ color: #ce1b4c;
- :after {
- content: '*';
- }
+ :after {
+ content: '*';
+ }
`;
const AtAndBrowserDetailsModal = ({
- show = false,
- isAdmin = false,
- firstLoad = true,
- atName = '',
- atVersion = '',
- atVersions = [],
- browserName = '',
- browserVersion = '',
- patternName = '', // admin related prop
- testerName = '', // admin related prop
- exactAtVersion = null,
- handleAction = () => {},
- handleClose = () => {}
+ show = false,
+ isAdmin = false,
+ firstLoad = true,
+ atName = '',
+ atVersion = '',
+ atVersions = [],
+ browserName = '',
+ browserVersion = '',
+ patternName = '', // admin related prop
+ testerName = '', // admin related prop
+ exactAtVersion = null,
+ handleAction = () => {},
+ handleClose = () => {}
}) => {
- // Only take into account the major version
- // of the provided browser version
- if (browserVersion.includes('.')) {
- browserVersion = browserVersion.split('.')[0];
+ // Only take into account the major version
+ // of the provided browser version
+ if (browserVersion.includes('.')) {
+ browserVersion = browserVersion.split('.')[0];
+ }
+ // Detect UA information
+ const { uaBrowser, uaMajor, uaMinor, uaPatch } = useDetectUa();
+
+ const navigate = useNavigate();
+
+ const updatedAtVersionDropdownRef = useRef();
+ const updatedBrowserVersionTextRef = useRef();
+
+ const [showExitModal, setShowExitModal] = useState(false);
+ const [isFirstLoad, setIsFirstLoad] = useState(true);
+ const [updatedAtVersion, setUpdatedAtVersion] = useState(
+ exactAtVersion ? exactAtVersion.name : 'Select a Version'
+ );
+ const [updatedBrowserVersion, setUpdatedBrowserVersion] = useState('');
+
+ const [isAtVersionError, setIsAtVersionError] = useState(false);
+ const [isBrowserVersionError, setIsBrowserVersionError] = useState(false);
+
+ const [
+ forceBrowserVersionUpdateMessage,
+ setForceBrowserVersionUpdateMessage
+ ] = useState(false);
+ const [browserVersionMismatchMessage, setBrowserVersionMismatchMessage] =
+ useState(false);
+
+ useEffect(() => {
+ setIsFirstLoad(firstLoad);
+ if (!firstLoad) {
+ setUpdatedAtVersion(atVersion);
+ setUpdatedBrowserVersion(browserVersion);
+ }
+ if (uaMajor === '0') {
+ setUpdatedBrowserVersion('');
+ updatedBrowserVersionTextRef.current.focus();
}
- // Detect UA information
- const { uaBrowser, uaMajor, uaMinor, uaPatch } = useDetectUa();
-
- const navigate = useNavigate();
-
- const updatedAtVersionDropdownRef = useRef();
- const updatedBrowserVersionTextRef = useRef();
-
- const [showExitModal, setShowExitModal] = useState(false);
- const [isFirstLoad, setIsFirstLoad] = useState(true);
- const [updatedAtVersion, setUpdatedAtVersion] = useState(
- exactAtVersion ? exactAtVersion.name : 'Select a Version'
- );
- const [updatedBrowserVersion, setUpdatedBrowserVersion] = useState('');
-
- const [isAtVersionError, setIsAtVersionError] = useState(false);
- const [isBrowserVersionError, setIsBrowserVersionError] = useState(false);
-
- const [
- forceBrowserVersionUpdateMessage,
- setForceBrowserVersionUpdateMessage
- ] = useState(false);
- const [browserVersionMismatchMessage, setBrowserVersionMismatchMessage] =
- useState(false);
-
- useEffect(() => {
- setIsFirstLoad(firstLoad);
- if (!firstLoad) {
- setUpdatedAtVersion(atVersion);
- setUpdatedBrowserVersion(browserVersion);
- }
- if (uaMajor === '0') {
- setUpdatedBrowserVersion('');
- updatedBrowserVersionTextRef.current.focus();
- }
- const foundBrowserVersion =
- uaBrowser === browserName && uaMajor !== '0' && uaMajor;
-
- if (
- // don't force browserVersion update with admin (unless first run)
- (!isAdmin || (isAdmin && firstLoad)) &&
- // check that saved browserVersion is the same as detected
- !browserVersion.includes(uaMajor) &&
- uaBrowser === browserName &&
- foundBrowserVersion
- ) {
- setForceBrowserVersionUpdateMessage(true);
- setUpdatedBrowserVersion(foundBrowserVersion);
- }
- }, [uaBrowser, uaMajor, uaMinor, uaPatch]);
-
- useEffect(() => {
- // check to support Tester Scenario 5
- if (uaMajor !== '0' && !updatedBrowserVersion.includes(`${uaMajor}`))
- setBrowserVersionMismatchMessage(true);
- else setBrowserVersionMismatchMessage(!isAdmin && false);
- }, [updatedBrowserVersion, uaMajor, uaMinor, uaPatch]);
-
- useEffect(() => {
- const forceFocusOnBrowserVersion =
- !isFirstLoad && !isAdmin && forceBrowserVersionUpdateMessage;
-
- if (forceFocusOnBrowserVersion)
- updatedBrowserVersionTextRef.current.focus();
- }, [forceBrowserVersionUpdateMessage]);
-
- const handleAtVersionChange = e => {
- const value = e.target.value;
- setUpdatedAtVersion(value);
- setIsAtVersionError(false);
- };
-
- const handleBrowserVersionChange = e => {
- const value = e.target.value;
-
- setUpdatedBrowserVersion(value);
-
- // remove message once browser has been changed
- !isAdmin && setForceBrowserVersionUpdateMessage(false);
- setIsBrowserVersionError(false);
- };
-
- const onSubmit = () => {
- // Passed action prop should account for AtVersion & BrowserVersion
- const updatedAtVersionError = updatedAtVersion === 'Select a Version';
- const isBrowserVersionTextError = !updatedBrowserVersion;
-
- if (updatedAtVersionError || isBrowserVersionTextError) {
- setIsAtVersionError(updatedAtVersionError);
- updatedAtVersionDropdownRef.current.focus();
-
- setIsBrowserVersionError(isBrowserVersionTextError);
- if (!updatedAtVersionError)
- updatedBrowserVersionTextRef.current.focus();
- return;
- }
+ const foundBrowserVersion =
+ uaBrowser === browserName && uaMajor !== '0' && uaMajor;
+
+ if (
+ // don't force browserVersion update with admin (unless first run)
+ (!isAdmin || (isAdmin && firstLoad)) &&
+ // check that saved browserVersion is the same as detected
+ !browserVersion.includes(uaMajor) &&
+ uaBrowser === browserName &&
+ foundBrowserVersion
+ ) {
+ setForceBrowserVersionUpdateMessage(true);
+ setUpdatedBrowserVersion(foundBrowserVersion);
+ }
+ }, [uaBrowser, uaMajor, uaMinor, uaPatch]);
+
+ useEffect(() => {
+ // check to support Tester Scenario 5
+ if (uaMajor !== '0' && !updatedBrowserVersion.includes(`${uaMajor}`))
+ setBrowserVersionMismatchMessage(true);
+ else setBrowserVersionMismatchMessage(!isAdmin && false);
+ }, [updatedBrowserVersion, uaMajor, uaMinor, uaPatch]);
+
+ useEffect(() => {
+ const forceFocusOnBrowserVersion =
+ !isFirstLoad && !isAdmin && forceBrowserVersionUpdateMessage;
+
+ if (forceFocusOnBrowserVersion)
+ updatedBrowserVersionTextRef.current.focus();
+ }, [forceBrowserVersionUpdateMessage]);
+
+ const handleAtVersionChange = e => {
+ const value = e.target.value;
+ setUpdatedAtVersion(value);
+ setIsAtVersionError(false);
+ };
+
+ const handleBrowserVersionChange = e => {
+ const value = e.target.value;
+
+ setUpdatedBrowserVersion(value);
+
+ // remove message once browser has been changed
+ !isAdmin && setForceBrowserVersionUpdateMessage(false);
+ setIsBrowserVersionError(false);
+ };
+
+ const onSubmit = () => {
+ // Passed action prop should account for AtVersion & BrowserVersion
+ const updatedAtVersionError = updatedAtVersion === 'Select a Version';
+ const isBrowserVersionTextError = !updatedBrowserVersion;
+
+ if (updatedAtVersionError || isBrowserVersionTextError) {
+ setIsAtVersionError(updatedAtVersionError);
+ updatedAtVersionDropdownRef.current.focus();
+
+ setIsBrowserVersionError(isBrowserVersionTextError);
+ if (!updatedAtVersionError) updatedBrowserVersionTextRef.current.focus();
+ return;
+ }
- let updateMessage = null;
- if (isFirstLoad)
- updateMessage =
- 'Your AT and Browser versions have been successfully saved.';
- else {
- if (
- updatedAtVersion !== atVersion &&
- updatedBrowserVersion !== browserVersion
- )
- updateMessage = (
- <>
- Your version of {atName} has been updated to{' '}
- {updatedAtVersion} . Your previous version was{' '}
- {atVersion} .
-
- Your version of {browserName} has been updated to{' '}
- {updatedBrowserVersion} . Your previous version
- was {browserVersion} .
- >
- );
- else if (updatedAtVersion !== atVersion)
- updateMessage = (
- <>
- Your version of {atName} has been updated to{' '}
- {updatedAtVersion} . Your previous version was{' '}
- {atVersion} .
- >
- );
- else if (updatedBrowserVersion !== browserVersion)
- updateMessage = (
- <>
- Your version of {browserName} has been updated to{' '}
- {updatedBrowserVersion} . Your previous version
- was {browserVersion} .
- >
- );
- }
+ let updateMessage = null;
+ if (isFirstLoad)
+ updateMessage =
+ 'Your AT and Browser versions have been successfully saved.';
+ else {
+ if (
+ updatedAtVersion !== atVersion &&
+ updatedBrowserVersion !== browserVersion
+ )
+ updateMessage = (
+ <>
+ Your version of {atName} has been updated to{' '}
+ {updatedAtVersion} . Your previous version was{' '}
+ {atVersion} .
+
+ Your version of {browserName} has been updated to{' '}
+ {updatedBrowserVersion} . Your previous version was{' '}
+ {browserVersion} .
+ >
+ );
+ else if (updatedAtVersion !== atVersion)
+ updateMessage = (
+ <>
+ Your version of {atName} has been updated to{' '}
+ {updatedAtVersion} . Your previous version was{' '}
+ {atVersion} .
+ >
+ );
+ else if (updatedBrowserVersion !== browserVersion)
+ updateMessage = (
+ <>
+ Your version of {browserName} has been updated to{' '}
+ {updatedBrowserVersion} . Your previous version was{' '}
+ {browserVersion} .
+ >
+ );
+ }
- // Save full version even though we don't display it.
- // Discard trailing zeros in found minor and patches.
- // This also accounts for trailing numbers after the patch.
- let fullVersion = '';
- let [majorVersion, minorVersion = '0', patchVersion = '0', ...rest] =
- updatedBrowserVersion.split('.');
-
- if (minorVersion === '0' && patchVersion === '0') {
- fullVersion = `${majorVersion}`;
- } else if (patchVersion === '0') {
- fullVersion = `${majorVersion}.${minorVersion}`;
- } else if (rest === undefined || rest.length === 0) {
- fullVersion = `${majorVersion}.${minorVersion}.${patchVersion}`;
- } else {
- fullVersion = `${majorVersion}.${minorVersion}.${patchVersion}.${rest.join(
- '.'
- )}`;
- }
+ // Save full version even though we don't display it.
+ // Discard trailing zeros in found minor and patches.
+ // This also accounts for trailing numbers after the patch.
+ let fullVersion = '';
+ let [majorVersion, minorVersion = '0', patchVersion = '0', ...rest] =
+ updatedBrowserVersion.split('.');
+
+ if (minorVersion === '0' && patchVersion === '0') {
+ fullVersion = `${majorVersion}`;
+ } else if (patchVersion === '0') {
+ fullVersion = `${majorVersion}.${minorVersion}`;
+ } else if (rest === undefined || rest.length === 0) {
+ fullVersion = `${majorVersion}.${minorVersion}.${patchVersion}`;
+ } else {
+ fullVersion = `${majorVersion}.${minorVersion}.${patchVersion}.${rest.join(
+ '.'
+ )}`;
+ }
- handleAction(updatedAtVersion, fullVersion, updateMessage);
- };
-
- const handleHide = () => setShowExitModal(true);
-
- // All scenarios here are based on
- // https://github.com/w3c/aria-at-app/issues/406 &
- // https://github.com/w3c/aria-at-app/issues/436
- return (
- <>
- setShowExitModal(true);
+
+ // All scenarios here are based on
+ // https://github.com/w3c/aria-at-app/issues/406 &
+ // https://github.com/w3c/aria-at-app/issues/436
+ return (
+ <>
+
+ You will be taken back to the Test Queue . Any changes you
+ made to your Assistive Technology and Browser versions will not be
+ saved.
+ >
+ }
+ actions={[
+ {
+ label: 'Ok',
+ onClick: () => navigate('/test-queue')
+ }
+ ]}
+ closeLabel="Cancel"
+ handleClose={() => setShowExitModal(false)}
+ staticBackdrop={true}
+ />
+
+ {!showExitModal && (
+
+ {/* Admin Scenario 1 */}
+ {isAdmin && (
+
+
+
+ You are about to review {patternName} as{' '}
+ {testerName} . Your Assistive Technology and Browser
+ might be different. Please proceed with caution.
+
+
+ )}
+ Your Assistive Technology and Browser Details are the following.
+ Please make sure this information is still accurate.
+
+
+
+
+
+ Assistive Technology Details
+
+
+ {/* Tester Scenario 6 */}
+ {!isFirstLoad && updatedAtVersion !== atVersion && (
+
+
+
+ You have selected {updatedAtVersion} for{' '}
+ {atName} . This version is different from the one
+ previously selected which was {atVersion} .
+
+
+ This change doesn't affect results that have already
+ been submitted for this plan. However, results you submit
+ during this session will be recorded with the versions
+ specified in this form.
+
+
+ )}
+ {exactAtVersion ? (
+
+
+
+ Results collected for this test plan require{' '}
+
+ {atName} {updatedAtVersion}
+
+ . By continuing, you confirm that your results are being
+ recorded using the specified version of the Assistive
+ Technology.
+
+
+ ) : null}
+
+ Assistive Technology Name
+
+
+
+ {exactAtVersion ? (
<>
- You will be taken back to the Test Queue . Any
- changes you made to your Assistive Technology and
- Browser versions will not be saved.
+ Assistive Technology Version
+
>
- }
- actions={[
- {
- label: 'Ok',
- onClick: () => navigate('/test-queue')
- }
- ]}
- closeLabel="Cancel"
- handleClose={() => setShowExitModal(false)}
- staticBackdrop={true}
- />
-
- {!showExitModal && (
-
- {/* Admin Scenario 1 */}
- {isAdmin && (
-
-
-
- You are about to review{' '}
- {patternName} as{' '}
- {testerName} . Your Assistive
- Technology and Browser might be
- different. Please proceed with caution.
-
-
- )}
- Your Assistive Technology and Browser Details are
- the following. Please make sure this information is
- still accurate.
-
-
-
-
-
- Assistive Technology Details
-
-
- {/* Tester Scenario 6 */}
- {!isFirstLoad &&
- updatedAtVersion !== atVersion && (
-
-
-
- You have selected{' '}
- {updatedAtVersion} for{' '}
- {atName} . This version is
- different from the one
- previously selected which was{' '}
- {atVersion} .
-
-
- This change doesn't affect
- results that have already been
- submitted for this plan.
- However, results you submit
- during this session will be
- recorded with the versions
- specified in this form.
-
-
- )}
- {exactAtVersion ? (
-
-
-
- Results collected for this test plan
- require{' '}
-
- {atName} {updatedAtVersion}
-
- . By continuing, you confirm that
- your results are being recorded
- using the specified version of the
- Assistive Technology.
-
-
- ) : null}
-
-
- Assistive Technology Name
-
-
-
-
- {exactAtVersion ? (
- <>
-
- Assistive Technology Version
-
-
- >
- ) : (
- <>
-
- Assistive Technology Version
-
-
-
- {[
- 'Select a Version',
- ...atVersions
- ].map(item => (
-
- {item}
-
- ))}
-
- {isAtVersionError && (
-
- Please select an Assistive
- Technology Version.
-
- )}
- >
- )}
-
-
-
-
-
- Browser Details
-
-
- {/* Tester Scenario 1 */}
- {isFirstLoad &&
- uaBrowser &&
- uaMajor !== '0' && (
-
-
-
- We have automatically detected
- your version of {uaBrowser}
-
-
- )}
- {/* Tester Scenario 3 */}
- {!isFirstLoad &&
- !isAdmin &&
- forceBrowserVersionUpdateMessage && (
-
-
-
- We have automatically detected
- you are using a different
- version of {uaBrowser} {' '}
- and we have updated it below.
- The previous version you were
- using was{' '}
- {browserVersion} .
-
-
- This change doesn't affect
- results that have already been
- submitted for this plan.
- However, results you submit
- during this session will be
- recorded with the versions
- specified in this form.
-
-
- )}
- {isFirstLoad &&
- uaBrowser !== browserName &&
- uaMajor !== '0' && (
-
-
-
- We have automatically detected
- you are using{' '}
-
- {uaBrowser} {uaMajor}
-
- . This test plan requires{' '}
- {browserName} . If you are
- recording results on behalf of
- someone else, please provide the
- Browser version below.
-
-
- )}
- {/* Tester Scenario 4 */}
- {!isAdmin &&
- !isFirstLoad &&
- uaBrowser !== browserName &&
- uaMajor !== '0' && (
-
-
-
- We have automatically detected
- you are now using{' '}
-
- {uaBrowser} {uaMajor}
-
- , which is a different browser
- from the last one you were
- testing with, which was{' '}
-
- {browserName}{' '}
- {browserVersion}
-
- .
-
-
- You can't edit your Browser
- type, but you can continue with{' '}
-
- {uaBrowser} {uaMajor}
-
- . Keep in mind that your test
- results will be recorded as if
- you were still using{' '}
-
- {browserName}{' '}
- {browserVersion}
-
- .
-
-
- )}
- {/* Tester Scenario 5 */}
- {!isAdmin &&
- uaBrowser === browserName &&
- browserVersionMismatchMessage && (
-
-
-
- The version of {browserName} you
- have set is different from the
- one we have automatically
- detected, which is{' '}
- {uaMajor}
- .
-
-
- This change doesn't affect
- results that have already been
- submitted for this plan.
- However, results you submit
- during this session will be
- recorded with the versions
- specified in this form.
-
-
- )}
- {/* Tester Scenario 7 */}
- {uaMajor === '0' && (
-
-
-
- We could not automatically detect
- what version of {browserName} {' '}
- you are using. Before continuing,
- please provide your version number.
-
-
- )}
- {/* Admin Scenario 1 */}
- {isAdmin &&
- (uaBrowser !== browserName ||
- browserVersionMismatchMessage) && (
-
-
-
- We have automatically detected
- you are using{' '}
-
- {uaBrowser} {uaMajor}
-
- . This version is different than
- what {testerName} was
- using last time, which was{' '}
-
- {browserName}{' '}
- {browserVersion}
-
- .
-
-
- We have not updated this
- information below. Please
- proceed with caution.
-
-
- )}
-
- Browser Name
-
-
-
-
-
- Browser Version
-
-
-
- {isBrowserVersionError && (
-
- Please enter a valid Browser
- Version.
-
- )}
-
-
-
- }
- actions={[
- {
- label:
- updatedAtVersion !== atVersion ||
- updatedBrowserVersion !== browserVersion
- ? 'Save and Continue'
- : 'Continue',
- onClick:
- updatedAtVersion !== atVersion ||
- updatedBrowserVersion !== browserVersion
- ? onSubmit
- : handleClose
- }
- ]}
- handleClose={!isFirstLoad ? handleClose : null}
- handleHide={handleHide}
- staticBackdrop={true}
- useOnHide={true}
- />
- )}
- >
- );
+ ) : (
+ <>
+
+ Assistive Technology Version
+
+
+
+ {['Select a Version', ...atVersions].map(item => (
+
+ {item}
+
+ ))}
+
+ {isAtVersionError && (
+
+ Please select an Assistive Technology Version.
+
+ )}
+ >
+ )}
+
+
+
+
+ Browser Details
+
+ {/* Tester Scenario 1 */}
+ {isFirstLoad && uaBrowser && uaMajor !== '0' && (
+
+
+
+ We have automatically detected your version of {uaBrowser}
+
+
+ )}
+ {/* Tester Scenario 3 */}
+ {!isFirstLoad &&
+ !isAdmin &&
+ forceBrowserVersionUpdateMessage && (
+
+
+
+ We have automatically detected you are using a different
+ version of {uaBrowser} and we have updated it
+ below. The previous version you were using was{' '}
+ {browserVersion} .
+
+
+ This change doesn't affect results that have
+ already been submitted for this plan. However, results
+ you submit during this session will be recorded with the
+ versions specified in this form.
+
+
+ )}
+ {isFirstLoad &&
+ uaBrowser !== browserName &&
+ uaMajor !== '0' && (
+
+
+
+ We have automatically detected you are using{' '}
+
+ {uaBrowser} {uaMajor}
+
+ . This test plan requires {browserName} . If you
+ are recording results on behalf of someone else, please
+ provide the Browser version below.
+
+
+ )}
+ {/* Tester Scenario 4 */}
+ {!isAdmin &&
+ !isFirstLoad &&
+ uaBrowser !== browserName &&
+ uaMajor !== '0' && (
+
+
+
+ We have automatically detected you are now using{' '}
+
+ {uaBrowser} {uaMajor}
+
+ , which is a different browser from the last one you
+ were testing with, which was{' '}
+
+ {browserName} {browserVersion}
+
+ .
+
+
+ You can't edit your Browser type, but you can
+ continue with{' '}
+
+ {uaBrowser} {uaMajor}
+
+ . Keep in mind that your test results will be recorded
+ as if you were still using{' '}
+
+ {browserName} {browserVersion}
+
+ .
+
+
+ )}
+ {/* Tester Scenario 5 */}
+ {!isAdmin &&
+ uaBrowser === browserName &&
+ browserVersionMismatchMessage && (
+
+
+
+ The version of {browserName} you have set is different
+ from the one we have automatically detected, which is{' '}
+ {uaMajor}
+ .
+
+
+ This change doesn't affect results that have
+ already been submitted for this plan. However, results
+ you submit during this session will be recorded with the
+ versions specified in this form.
+
+
+ )}
+ {/* Tester Scenario 7 */}
+ {uaMajor === '0' && (
+
+
+
+ We could not automatically detect what version of{' '}
+ {browserName} you are using. Before continuing,
+ please provide your version number.
+
+
+ )}
+ {/* Admin Scenario 1 */}
+ {isAdmin &&
+ (uaBrowser !== browserName ||
+ browserVersionMismatchMessage) && (
+
+
+
+ We have automatically detected you are using{' '}
+
+ {uaBrowser} {uaMajor}
+
+ . This version is different than what{' '}
+ {testerName} was using last time, which was{' '}
+
+ {browserName} {browserVersion}
+
+ .
+
+
+ We have not updated this information below. Please
+ proceed with caution.
+
+
+ )}
+
+ Browser Name
+
+
+
+
+
+ Browser Version
+
+
+
+ {isBrowserVersionError && (
+
+ Please enter a valid Browser Version.
+
+ )}
+
+
+
+ }
+ actions={[
+ {
+ label:
+ updatedAtVersion !== atVersion ||
+ updatedBrowserVersion !== browserVersion
+ ? 'Save and Continue'
+ : 'Continue',
+ onClick:
+ updatedAtVersion !== atVersion ||
+ updatedBrowserVersion !== browserVersion
+ ? onSubmit
+ : handleClose
+ }
+ ]}
+ handleClose={!isFirstLoad ? handleClose : null}
+ handleHide={handleHide}
+ staticBackdrop={true}
+ useOnHide={true}
+ />
+ )}
+ >
+ );
};
AtAndBrowserDetailsModal.propTypes = {
- show: PropTypes.bool,
- isAdmin: PropTypes.bool,
- firstLoad: PropTypes.bool,
- atName: PropTypes.string,
- atVersion: PropTypes.string,
- atVersions: PropTypes.arrayOf(PropTypes.string),
- browserName: PropTypes.string,
- browserVersion: PropTypes.string,
- browserVersions: PropTypes.arrayOf(PropTypes.string),
- patternName: PropTypes.string,
- testerName: PropTypes.string,
- exactAtVersion: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired
- }),
- handleClose: PropTypes.func,
- handleAction: PropTypes.func
+ show: PropTypes.bool,
+ isAdmin: PropTypes.bool,
+ firstLoad: PropTypes.bool,
+ atName: PropTypes.string,
+ atVersion: PropTypes.string,
+ atVersions: PropTypes.arrayOf(PropTypes.string),
+ browserName: PropTypes.string,
+ browserVersion: PropTypes.string,
+ browserVersions: PropTypes.arrayOf(PropTypes.string),
+ patternName: PropTypes.string,
+ testerName: PropTypes.string,
+ exactAtVersion: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+ }),
+ handleClose: PropTypes.func,
+ handleAction: PropTypes.func
};
export default AtAndBrowserDetailsModal;
diff --git a/client/components/common/AtBrowserVersion/index.jsx b/client/components/common/AtBrowserVersion/index.jsx
index 0d0e49a97..6398e2728 100644
--- a/client/components/common/AtBrowserVersion/index.jsx
+++ b/client/components/common/AtBrowserVersion/index.jsx
@@ -3,52 +3,52 @@ import PropTypes from 'prop-types';
import styled from '@emotion/styled';
const VersionContainer = styled.div`
+ display: inline-block;
+ flex-wrap: wrap;
+ background: #f5f5f5;
+ border-radius: 4px;
+ padding: 0 5px;
+ font-weight: bold;
+
+ & span {
+ font-weight: initial;
display: inline-block;
- flex-wrap: wrap;
- background: #f5f5f5;
- border-radius: 4px;
- padding: 0 5px;
- font-weight: bold;
-
- & span {
- font-weight: initial;
- display: inline-block;
- margin-left: 2px;
- }
+ margin-left: 2px;
+ }
`;
const AtVersion = ({ at, minimumAtVersion, exactAtVersion }) => {
- const atVersionFormatted = minimumAtVersion
- ? `${minimumAtVersion.name} or later`
- : exactAtVersion.name;
-
- return (
-
- {at.name}
- {atVersionFormatted}
-
- );
+ const atVersionFormatted = minimumAtVersion
+ ? `${minimumAtVersion.name} or later`
+ : exactAtVersion.name;
+
+ return (
+
+ {at.name}
+ {atVersionFormatted}
+
+ );
};
AtVersion.propTypes = {
- at: PropTypes.shape({ name: PropTypes.string.isRequired }).isRequired,
- minimumAtVersion: PropTypes.shape({ name: PropTypes.string.isRequired }),
- exactAtVersion: PropTypes.shape({ name: PropTypes.string.isRequired })
+ at: PropTypes.shape({ name: PropTypes.string.isRequired }).isRequired,
+ minimumAtVersion: PropTypes.shape({ name: PropTypes.string.isRequired }),
+ exactAtVersion: PropTypes.shape({ name: PropTypes.string.isRequired })
};
const BrowserVersion = ({ browser }) => {
- return (
-
- {browser.name}
- Any version
-
- );
+ return (
+
+ {browser.name}
+ Any version
+
+ );
};
BrowserVersion.propTypes = {
- browser: PropTypes.shape({
- name: PropTypes.string.isRequired
- }).isRequired
+ browser: PropTypes.shape({
+ name: PropTypes.string.isRequired
+ }).isRequired
};
export { AtVersion, BrowserVersion };
diff --git a/client/components/common/BasicModal/BasicModal.css b/client/components/common/BasicModal/BasicModal.css
index c602831e3..37afcaa0c 100644
--- a/client/components/common/BasicModal/BasicModal.css
+++ b/client/components/common/BasicModal/BasicModal.css
@@ -1,19 +1,19 @@
/* Allow multiple dialogs to be open at the same time */
div[role='dialog'][aria-modal='true']:nth-last-child(1) {
- z-index: 1125;
+ z-index: 1125;
}
.modal-backdrop.show:nth-last-child(2) {
- z-index: 1100;
+ z-index: 1100;
}
div[role='dialog'][aria-modal='true']:nth-last-child(3) {
- z-index: 1075;
+ z-index: 1075;
}
.modal-backdrop.show:nth-last-child(4) {
- z-index: 1050;
+ z-index: 1050;
}
div[role='dialog'][aria-modal='true']:nth-last-child(5) {
- z-index: 1025;
+ z-index: 1025;
}
.modal-backdrop.show:nth-last-child(6) {
- z-index: 1000;
+ z-index: 1000;
}
diff --git a/client/components/common/BasicModal/index.jsx b/client/components/common/BasicModal/index.jsx
index 89a59ee97..11d487264 100644
--- a/client/components/common/BasicModal/index.jsx
+++ b/client/components/common/BasicModal/index.jsx
@@ -8,147 +8,143 @@ import { uniqueId } from 'lodash';
import './BasicModal.css';
const ModalTitleStyle = styled.h1`
- border: 0;
- padding: 0;
- font-size: 1.5rem;
+ border: 0;
+ padding: 0;
+ font-size: 1.5rem;
`;
const BasicModal = ({
- show = false,
- centered = false,
- animation = true,
- closeButton = true,
- cancelButton = true,
- headerSep = true,
- showFooter = true,
- dialogClassName = '',
- title = null,
- content = null,
- closeLabel = 'Cancel',
- handleClose = null,
- handleHide = null,
- staticBackdrop = false,
- useOnHide = false,
- actions = [],
- initialFocusRef = null
+ show = false,
+ centered = false,
+ animation = true,
+ closeButton = true,
+ cancelButton = true,
+ headerSep = true,
+ showFooter = true,
+ dialogClassName = '',
+ title = null,
+ content = null,
+ closeLabel = 'Cancel',
+ handleClose = null,
+ handleHide = null,
+ staticBackdrop = false,
+ useOnHide = false,
+ actions = [],
+ initialFocusRef = null
}) => {
- const headerRef = useRef();
+ const headerRef = useRef();
- useEffect(() => {
- if (!show) return;
- if (initialFocusRef?.current) {
- initialFocusRef.current.focus();
- } else {
- headerRef.current.focus();
- }
- }, [show]);
+ useEffect(() => {
+ if (!show) return;
+ if (initialFocusRef?.current) {
+ initialFocusRef.current.focus();
+ } else {
+ headerRef.current.focus();
+ }
+ }, [show]);
- const id = useMemo(() => {
- return uniqueId('modal-');
- }, []);
+ const id = useMemo(() => {
+ return uniqueId('modal-');
+ }, []);
- const renderAction = (action, index) => {
- if (action.component) {
- return React.createElement(
- action.component,
- { key: `CustomComponent_${index}`, ...action.props },
- null
- );
- } else {
- return (
-
- {action.label ?? 'Continue'}
-
- );
- }
- };
+ const renderAction = (action, index) => {
+ if (action.component) {
+ return React.createElement(
+ action.component,
+ { key: `CustomComponent_${index}`, ...action.props },
+ null
+ );
+ } else {
+ return (
+
+ {action.label ?? 'Continue'}
+
+ );
+ }
+ };
- return (
-
+ {}}
+ onExit={!useOnHide ? handleHide || handleClose : () => {}}
+ /* Disabled due to buggy implementation which jumps the page */
+ autoFocus={false}
+ aria-labelledby={`title-${id}`}
+ dialogClassName={dialogClassName}
+ backdrop={staticBackdrop ? 'static' : true}
+ >
+
-
-
-
- {title}
-
-
- {content}
- {showFooter && (
-
- {cancelButton && handleClose && (
-
- {closeLabel}
-
- )}
- {actions.map((action, index) =>
- renderAction(action, index)
- )}
-
- )}
-
-
- );
+
+ {title}
+
+
+ {content}
+ {showFooter && (
+
+ {cancelButton && handleClose && (
+
+ {closeLabel}
+
+ )}
+ {actions.map((action, index) => renderAction(action, index))}
+
+ )}
+
+
+ );
};
BasicModal.propTypes = {
- show: PropTypes.bool,
- centered: PropTypes.bool,
- animation: PropTypes.bool,
- closeButton: PropTypes.bool,
- cancelButton: PropTypes.bool,
- headerSep: PropTypes.bool,
- showFooter: PropTypes.bool,
- dialogClassName: PropTypes.string,
- title: PropTypes.node.isRequired,
- content: PropTypes.node,
- closeLabel: PropTypes.string,
- handleClose: PropTypes.func,
- handleHide: PropTypes.func,
- staticBackdrop: PropTypes.bool,
- useOnHide: PropTypes.bool,
- actions: PropTypes.arrayOf(
- PropTypes.shape({
- label: PropTypes.string,
- onClick: PropTypes.func,
- variant: PropTypes.string,
- className: PropTypes.string,
- component: PropTypes.elementType,
- props: PropTypes.object
- })
- ),
- initialFocusRef: PropTypes.shape({
- current: PropTypes.any
+ show: PropTypes.bool,
+ centered: PropTypes.bool,
+ animation: PropTypes.bool,
+ closeButton: PropTypes.bool,
+ cancelButton: PropTypes.bool,
+ headerSep: PropTypes.bool,
+ showFooter: PropTypes.bool,
+ dialogClassName: PropTypes.string,
+ title: PropTypes.node.isRequired,
+ content: PropTypes.node,
+ closeLabel: PropTypes.string,
+ handleClose: PropTypes.func,
+ handleHide: PropTypes.func,
+ staticBackdrop: PropTypes.bool,
+ useOnHide: PropTypes.bool,
+ actions: PropTypes.arrayOf(
+ PropTypes.shape({
+ label: PropTypes.string,
+ onClick: PropTypes.func,
+ variant: PropTypes.string,
+ className: PropTypes.string,
+ component: PropTypes.elementType,
+ props: PropTypes.object
})
+ ),
+ initialFocusRef: PropTypes.shape({
+ current: PropTypes.any
+ })
};
export default BasicModal;
diff --git a/client/components/common/BasicThemedModal/index.jsx b/client/components/common/BasicThemedModal/index.jsx
index 788ac6966..d2dafffb2 100644
--- a/client/components/common/BasicThemedModal/index.jsx
+++ b/client/components/common/BasicThemedModal/index.jsx
@@ -7,132 +7,130 @@ import styled from '@emotion/styled';
import { uniqueId } from 'lodash';
const ModalTitleStyle = styled.h1`
- border: 0;
- padding: 0;
- font-size: 1.5rem;
+ border: 0;
+ padding: 0;
+ font-size: 1.5rem;
`;
const ModalInnerSectionContainer = styled.div`
- display: grid;
- grid-auto-flow: column;
+ display: grid;
+ grid-auto-flow: column;
- grid-template-columns: 50px auto;
- grid-gap: 10px;
+ grid-template-columns: 50px auto;
+ grid-gap: 10px;
`;
const ColorStrip = styled.div`
- width: 100%;
- height: 10px;
- ${props => props.hideHeadline && `display: none;`}
- background-color: ${({ theme }) =>
- theme === 'danger' ? '#ce1b4c' : '#fab700'};
+ width: 100%;
+ height: 10px;
+ ${props => props.hideHeadline && `display: none;`}
+ background-color: ${({ theme }) =>
+ theme === 'danger' ? '#ce1b4c' : '#fab700'};
- border-top-left-radius: calc(0.3rem - 1px);
- border-top-right-radius: calc(0.3rem - 1px);
+ border-top-left-radius: calc(0.3rem - 1px);
+ border-top-right-radius: calc(0.3rem - 1px);
`;
const BasicThemedModal = ({
- show = false,
- centered = false,
- animation = true,
- theme = 'warning', // warning, danger
- dialogClassName = '',
- title = null,
- content = null,
- actionButtons = [],
- closeLabel = 'Cancel',
- handleClose = null,
- showCloseAction = true
+ show = false,
+ centered = false,
+ animation = true,
+ theme = 'warning', // warning, danger
+ dialogClassName = '',
+ title = null,
+ content = null,
+ actionButtons = [],
+ closeLabel = 'Cancel',
+ handleClose = null,
+ showCloseAction = true
}) => {
- const headerRef = useRef();
+ const headerRef = useRef();
- useEffect(() => {
- if (!show) return;
- headerRef.current.focus();
- }, [show]);
+ useEffect(() => {
+ if (!show) return;
+ headerRef.current.focus();
+ }, [show]);
- const id = useMemo(() => {
- return uniqueId('modal-');
- }, []);
+ const id = useMemo(() => {
+ return uniqueId('modal-');
+ }, []);
- return (
- <>
-
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+ <>
+
+ {content}
+ >
+
+
+
+ {showCloseAction && (
+
+ {closeLabel}
+
+ )}
+ {actionButtons.map(({ action, text }) => (
+
-
-
-
-
-
- {title}
-
-
-
-
-
- <>
-
- {content}
- >
-
-
-
- {showCloseAction && (
-
- {closeLabel}
-
- )}
- {actionButtons.map(({ action, text }) => (
-
- {text}
-
- ))}
-
-
- >
- );
+ {text}
+
+ ))}
+
+
+ >
+ );
};
BasicThemedModal.propTypes = {
- show: PropTypes.bool,
- centered: PropTypes.bool,
- animation: PropTypes.bool,
- theme: PropTypes.string,
- dialogClassName: PropTypes.string,
- title: PropTypes.node.isRequired,
- content: PropTypes.node.isRequired,
- actionButtons: PropTypes.arrayOf(
- PropTypes.shape({
- text: PropTypes.string,
- action: PropTypes.func
- })
- ),
- closeLabel: PropTypes.string,
- handleClose: PropTypes.func,
- showCloseAction: PropTypes.bool
+ show: PropTypes.bool,
+ centered: PropTypes.bool,
+ animation: PropTypes.bool,
+ theme: PropTypes.string,
+ dialogClassName: PropTypes.string,
+ title: PropTypes.node.isRequired,
+ content: PropTypes.node.isRequired,
+ actionButtons: PropTypes.arrayOf(
+ PropTypes.shape({
+ text: PropTypes.string,
+ action: PropTypes.func
+ })
+ ),
+ closeLabel: PropTypes.string,
+ handleClose: PropTypes.func,
+ showCloseAction: PropTypes.bool
};
export default BasicThemedModal;
diff --git a/client/components/common/ClippedProgressBar/ClippedProgressBar.css b/client/components/common/ClippedProgressBar/ClippedProgressBar.css
index a0c483da1..d81763427 100644
--- a/client/components/common/ClippedProgressBar/ClippedProgressBar.css
+++ b/client/components/common/ClippedProgressBar/ClippedProgressBar.css
@@ -1,38 +1,38 @@
.progress {
- height: 1.2rem;
- border-radius: 1rem;
- margin-top: 0.2em;
- border: 1px solid gray;
+ height: 1.2rem;
+ border-radius: 1rem;
+ margin-top: 0.2em;
+ border: 1px solid gray;
}
.progress .progress-bar.bg-info {
- background-color: #0b60ab !important;
+ background-color: #0b60ab !important;
}
.progress.clipped {
- position: relative;
- display: flex;
- overflow: hidden;
+ position: relative;
+ display: flex;
+ overflow: hidden;
}
.progress.clipped .front {
- position: absolute;
- display: flex;
- justify-content: center;
- align-items: center;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- background: #e9ecef;
- color: black;
+ position: absolute;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background: #e9ecef;
+ color: black;
}
.progress.clipped .back {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- background: #0b60ab;
- color: white;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ background: #0b60ab;
+ color: white;
}
diff --git a/client/components/common/ClippedProgressBar/index.jsx b/client/components/common/ClippedProgressBar/index.jsx
index 057251c89..ebe7d8bcd 100644
--- a/client/components/common/ClippedProgressBar/index.jsx
+++ b/client/components/common/ClippedProgressBar/index.jsx
@@ -3,48 +3,46 @@ import PropTypes from 'prop-types';
import './ClippedProgressBar.css';
const ProgressBar = ({
- progress = 0,
- label = '',
- clipped = true,
- decorative
+ progress = 0,
+ label = '',
+ clipped = true,
+ decorative
}) => {
- return (
- <>
- {clipped ? (
-
-
- {decorative ? null : `${progress}%`}
-
-
- {decorative ? null : `${progress}%`}
-
-
- ) : (
-
-
- {decorative ? null : `${progress}%`}
-
-
- )}
- >
- );
+ return (
+ <>
+ {clipped ? (
+
+
+ {decorative ? null : `${progress}%`}
+
+
{decorative ? null : `${progress}%`}
+
+ ) : (
+
+
+ {decorative ? null : `${progress}%`}
+
+
+ )}
+ >
+ );
};
ProgressBar.propTypes = {
- progress: PropTypes.number,
- label: PropTypes.string,
- clipped: PropTypes.bool,
- decorative: PropTypes.bool
+ progress: PropTypes.number,
+ label: PropTypes.string,
+ clipped: PropTypes.bool,
+ decorative: PropTypes.bool
};
export default ProgressBar;
diff --git a/client/components/common/DeleteButton/index.jsx b/client/components/common/DeleteButton/index.jsx
index 0bd9cc24d..73dfb203a 100644
--- a/client/components/common/DeleteButton/index.jsx
+++ b/client/components/common/DeleteButton/index.jsx
@@ -6,29 +6,29 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrash } from '@fortawesome/free-solid-svg-icons';
const StyledDeleteButton = styled(Button)`
- color: #ce1b4c;
+ color: #ce1b4c;
- &:hover {
- background: #ce1b4c;
- color: white;
- }
+ &:hover {
+ background: #ce1b4c;
+ color: white;
+ }
`;
const DeleteButton = ({ onClick, ariaLabel }) => {
- return (
-
- Delete
-
- );
+ return (
+
+ Delete
+
+ );
};
DeleteButton.propTypes = {
- onClick: PropTypes.func.isRequired,
- ariaLabel: PropTypes.string.isRequired
+ onClick: PropTypes.func.isRequired,
+ ariaLabel: PropTypes.string.isRequired
};
export default DeleteButton;
diff --git a/client/components/common/DisclosureComponent/index.jsx b/client/components/common/DisclosureComponent/index.jsx
index 88ac56902..a0273aeae 100644
--- a/client/components/common/DisclosureComponent/index.jsx
+++ b/client/components/common/DisclosureComponent/index.jsx
@@ -5,21 +5,21 @@ import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
import PropTypes from 'prop-types';
const DisclosureParent = styled.div`
- border: 1px solid #d3d5da;
- border-radius: 3px;
- width: 100%;
+ border: 1px solid #d3d5da;
+ border-radius: 3px;
+ width: 100%;
- h1,
- h2,
- h3,
- h4 {
- margin: 0;
- padding: 0;
- }
+ h1,
+ h2,
+ h3,
+ h4 {
+ margin: 0;
+ padding: 0;
+ }
- ${({ stacked }) =>
- stacked &&
- `
+ ${({ stacked }) =>
+ stacked &&
+ `
h1:not(:first-of-type) button,
h2:not(:first-of-type) button,
h3:not(:first-of-type) button,
@@ -29,173 +29,167 @@ const DisclosureParent = styled.div`
`;
const DisclosureButton = styled.button`
- position: relative;
- width: 100%;
- margin: 0;
- padding: 1.25rem 40px 1.25rem 1.25rem;
- text-align: left;
- font-size: 1rem;
- font-weight: bold;
- border: none;
- border-radius: 3px;
- background-color: transparent;
+ position: relative;
+ width: 100%;
+ margin: 0;
+ padding: 1.25rem 40px 1.25rem 1.25rem;
+ text-align: left;
+ font-size: 1rem;
+ font-weight: bold;
+ border: none;
+ border-radius: 3px;
+ background-color: transparent;
- ${({ stacked }) =>
- stacked &&
- `
+ ${({ stacked }) =>
+ stacked &&
+ `
&:nth-last-of-type(1) {
border-radius: 0 0 3px 3px;
} `}
- &:hover,
+ &:hover,
&:focus {
- padding: 1.25rem;
- border: 0 solid #005a9c;
- background-color: #def;
- cursor: pointer;
- }
+ padding: 1.25rem;
+ border: 0 solid #005a9c;
+ background-color: #def;
+ cursor: pointer;
+ }
- .disclosure-icon {
- position: absolute;
- margin: 0;
- top: 50%;
- right: 1.25rem;
+ .disclosure-icon {
+ position: absolute;
+ margin: 0;
+ top: 50%;
+ right: 1.25rem;
- color: #969696;
- transform: translateY(-50%);
- }
+ color: #969696;
+ transform: translateY(-50%);
+ }
`;
const DisclosureContainer = styled.div`
- display: ${({ show, stacked }) =>
- show && stacked ? 'block' : show ? 'flex' : 'none'};
- flex-direction: ${({ stacked }) => !stacked && 'column'};
- gap: ${({ stacked }) => !stacked && '1.25rem'};
+ display: ${({ show, stacked }) =>
+ show && stacked ? 'block' : show ? 'flex' : 'none'};
+ flex-direction: ${({ stacked }) => !stacked && 'column'};
+ gap: ${({ stacked }) => !stacked && '1.25rem'};
- background-color: #f8f9fa;
- padding: ${({ component }) =>
- component === 'test-management' ? '0' : '1.25rem'};
- border-top: 1px solid #d3d5da;
+ background-color: #f8f9fa;
+ padding: ${({ component }) =>
+ component === 'test-management' ? '0' : '1.25rem'};
+ border-top: 1px solid #d3d5da;
`;
const DisclosureComponent = ({
- componentId,
- title = null,
- disclosureContainerView = null,
- onClick = null,
- expanded = false,
- stacked = false,
- headingLevel = '3',
- className = null
+ componentId,
+ title = null,
+ disclosureContainerView = null,
+ onClick = null,
+ expanded = false,
+ stacked = false,
+ headingLevel = '3',
+ className = null
}) => {
- const [isExpanded, setIsExpanded] = useState(expanded);
- const Tag = `h${headingLevel}`;
+ const [isExpanded, setIsExpanded] = useState(expanded);
+ const Tag = `h${headingLevel}`;
- return (
- <>
- {stacked ? (
-
- {title.map((_, index) => {
- const buttonTitle = title[index];
- const labelTitle =
- typeof buttonTitle === 'string'
- ? buttonTitle
- : index;
- const buttonExpanded = expanded[index];
- const buttonOnClick = onClick[index];
- const buttonDisclosureContainerView =
- disclosureContainerView[index];
+ return (
+ <>
+ {stacked ? (
+
+ {title.map((_, index) => {
+ const buttonTitle = title[index];
+ const labelTitle =
+ typeof buttonTitle === 'string' ? buttonTitle : index;
+ const buttonExpanded = expanded[index];
+ const buttonOnClick = onClick[index];
+ const buttonDisclosureContainerView =
+ disclosureContainerView[index];
- return (
-
-
-
- {buttonTitle}
-
-
-
-
- {buttonDisclosureContainerView}
-
-
- );
- })}
-
- ) : (
-
-
- setIsExpanded(!isExpanded)}
- >
- {title}
-
-
-
-
- {disclosureContainerView}
-
-
- )}
- >
- );
+ return (
+
+
+
+ {buttonTitle}
+
+
+
+
+ {buttonDisclosureContainerView}
+
+
+ );
+ })}
+
+ ) : (
+
+
+ setIsExpanded(!isExpanded)}
+ >
+ {title}
+
+
+
+
+ {disclosureContainerView}
+
+
+ )}
+ >
+ );
};
DisclosureComponent.propTypes = {
- componentId: PropTypes.string,
- title: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.node,
- PropTypes.arrayOf(PropTypes.string),
- PropTypes.arrayOf(PropTypes.node)
- ]),
- disclosureContainerView: PropTypes.oneOfType([
- PropTypes.node,
- PropTypes.arrayOf(PropTypes.node)
- ]),
- onClick: PropTypes.oneOfType([
- PropTypes.func,
- PropTypes.arrayOf(PropTypes.func)
- ]),
- expanded: PropTypes.oneOfType([
- PropTypes.bool,
- PropTypes.arrayOf(PropTypes.bool)
- ]),
- stacked: PropTypes.bool,
- headingLevel: PropTypes.string,
- className: PropTypes.string
+ componentId: PropTypes.string,
+ title: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.node,
+ PropTypes.arrayOf(PropTypes.string),
+ PropTypes.arrayOf(PropTypes.node)
+ ]),
+ disclosureContainerView: PropTypes.oneOfType([
+ PropTypes.node,
+ PropTypes.arrayOf(PropTypes.node)
+ ]),
+ onClick: PropTypes.oneOfType([
+ PropTypes.func,
+ PropTypes.arrayOf(PropTypes.func)
+ ]),
+ expanded: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.arrayOf(PropTypes.bool)
+ ]),
+ stacked: PropTypes.bool,
+ headingLevel: PropTypes.string,
+ className: PropTypes.string
};
export default DisclosureComponent;
diff --git a/client/components/common/FilterButtons/index.jsx b/client/components/common/FilterButtons/index.jsx
index d8938e81a..d9b35ea3c 100644
--- a/client/components/common/FilterButtons/index.jsx
+++ b/client/components/common/FilterButtons/index.jsx
@@ -4,64 +4,64 @@ import styled from '@emotion/styled';
import { Button } from 'react-bootstrap';
const StyledFilterButton = styled(Button)`
- background: #e9ebee;
- border-radius: 16px;
- margin-left: 12px;
- background-color: white;
- font-weight: 400;
+ background: #e9ebee;
+ border-radius: 16px;
+ margin-left: 12px;
+ background-color: white;
+ font-weight: 400;
- &.active,
- &:active {
- background: #eaf3fe !important;
- border: #517dbc 2px solid !important;
- box-shadow: none !important;
+ &.active,
+ &:active {
+ background: #eaf3fe !important;
+ border: #517dbc 2px solid !important;
+ box-shadow: none !important;
- &:hover,
- :focus {
- box-shadow: 0 0 0 0.2rem rgba(103, 171, 197, 0.5) !important;
- }
+ &:hover,
+ :focus {
+ box-shadow: 0 0 0 0.2rem rgba(103, 171, 197, 0.5) !important;
}
+ }
`;
const FilterButtons = ({
- filterLabel,
- filterAriaLabel,
- filterOptions,
- activeFilter,
- onFilterChange
+ filterLabel,
+ filterAriaLabel,
+ filterOptions,
+ activeFilter,
+ onFilterChange
}) => {
- return (
-
- {filterLabel}
- {Object.entries(filterOptions).map(([value, label]) => {
- const isActive = activeFilter === value;
- return (
-
- onFilterChange(value)}
- >
- {label}
-
-
- );
- })}
-
- );
+ return (
+
+ {filterLabel}
+ {Object.entries(filterOptions).map(([value, label]) => {
+ const isActive = activeFilter === value;
+ return (
+
+ onFilterChange(value)}
+ >
+ {label}
+
+
+ );
+ })}
+
+ );
};
FilterButtons.propTypes = {
- filterLabel: PropTypes.string.isRequired,
- filterAriaLabel: PropTypes.string,
- filterOptions: PropTypes.objectOf(PropTypes.string).isRequired,
- activeFilter: PropTypes.string.isRequired,
- onFilterChange: PropTypes.func.isRequired
+ filterLabel: PropTypes.string.isRequired,
+ filterAriaLabel: PropTypes.string,
+ filterOptions: PropTypes.objectOf(PropTypes.string).isRequired,
+ activeFilter: PropTypes.string.isRequired,
+ onFilterChange: PropTypes.func.isRequired
};
export default FilterButtons;
diff --git a/client/components/common/FocusTrapper/index.jsx b/client/components/common/FocusTrapper/index.jsx
index 60e27642f..af846e188 100644
--- a/client/components/common/FocusTrapper/index.jsx
+++ b/client/components/common/FocusTrapper/index.jsx
@@ -3,90 +3,89 @@ import PropTypes from 'prop-types';
import { useEffect, useRef } from 'react';
const FocusTrapper = ({ children, isActive, initialFocusRef, trappedElId }) => {
- const focusableElsRef = useRef([]);
- const updateFocusableElements = () => {
- if (focusableElsRef.current.length > 0) return;
+ const focusableElsRef = useRef([]);
+ const updateFocusableElements = () => {
+ if (focusableElsRef.current.length > 0) return;
- // Must use getElementById because a ref is not consistently populated
- const el = document.getElementById(trappedElId);
- if (!el) return;
+ // Must use getElementById because a ref is not consistently populated
+ const el = document.getElementById(trappedElId);
+ if (!el) return;
- focusableElsRef.current = Array.from(
- el.querySelectorAll(
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
- )
- );
+ focusableElsRef.current = Array.from(
+ el.querySelectorAll(
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
+ )
+ );
- // Create two elements to trap focus before and after the dialog
- // APG Example for Dialog used as reference
- const before = document.createElement('div');
- const after = document.createElement('div');
- before.tabIndex = 0;
- after.tabIndex = 0;
- el.prepend(before);
- el.append(after);
+ // Create two elements to trap focus before and after the dialog
+ // APG Example for Dialog used as reference
+ const before = document.createElement('div');
+ const after = document.createElement('div');
+ before.tabIndex = 0;
+ after.tabIndex = 0;
+ el.prepend(before);
+ el.append(after);
- focusableElsRef.current.unshift(before);
- focusableElsRef.current.push(after);
- };
+ focusableElsRef.current.unshift(before);
+ focusableElsRef.current.push(after);
+ };
- const trapFocus = event => {
- const isTabPressed = event.key === 'Tab' || event.keyCode === 9;
- if (!isTabPressed) return;
+ const trapFocus = event => {
+ const isTabPressed = event.key === 'Tab' || event.keyCode === 9;
+ if (!isTabPressed) return;
- const focusableEls = focusableElsRef.current;
+ const focusableEls = focusableElsRef.current;
- // First focusable element is after the blank 'before' div
- const firstFocusableEl = focusableEls[1];
- // Last focusable element is before the blank 'after' div
- const lastFocusableEl = focusableEls[focusableEls.length - 2];
- // When SHIFT + TAB is pressed and the active element is the first focusable element
- if (
- event.shiftKey &&
- (document.activeElement === firstFocusableEl ||
- document.activeElement === focusableEls[0] ||
- document.activeElement === initialFocusRef.current)
- ) {
- lastFocusableEl.focus();
- event.preventDefault();
- }
- // When TAB is pressed and the active element is the last focusable element
- else if (
- !event.shiftKey &&
- (document.activeElement === lastFocusableEl ||
- document.activeElement ===
- focusableEls[focusableEls.length - 1])
- ) {
- firstFocusableEl.focus();
- event.preventDefault();
- }
- };
+ // First focusable element is after the blank 'before' div
+ const firstFocusableEl = focusableEls[1];
+ // Last focusable element is before the blank 'after' div
+ const lastFocusableEl = focusableEls[focusableEls.length - 2];
+ // When SHIFT + TAB is pressed and the active element is the first focusable element
+ if (
+ event.shiftKey &&
+ (document.activeElement === firstFocusableEl ||
+ document.activeElement === focusableEls[0] ||
+ document.activeElement === initialFocusRef.current)
+ ) {
+ lastFocusableEl.focus();
+ event.preventDefault();
+ }
+ // When TAB is pressed and the active element is the last focusable element
+ else if (
+ !event.shiftKey &&
+ (document.activeElement === lastFocusableEl ||
+ document.activeElement === focusableEls[focusableEls.length - 1])
+ ) {
+ firstFocusableEl.focus();
+ event.preventDefault();
+ }
+ };
- useEffect(() => {
- if (isActive) {
- updateFocusableElements();
- document.addEventListener('keydown', trapFocus);
- } else {
- document.removeEventListener('keydown', trapFocus);
- focusableElsRef.current = [];
- }
+ useEffect(() => {
+ if (isActive) {
+ updateFocusableElements();
+ document.addEventListener('keydown', trapFocus);
+ } else {
+ document.removeEventListener('keydown', trapFocus);
+ focusableElsRef.current = [];
+ }
- return () => {
- focusableElsRef.current = [];
- document.removeEventListener('keydown', trapFocus);
- };
- }, [isActive]);
+ return () => {
+ focusableElsRef.current = [];
+ document.removeEventListener('keydown', trapFocus);
+ };
+ }, [isActive]);
- return {children}
;
+ return {children}
;
};
FocusTrapper.propTypes = {
- children: PropTypes.node.isRequired,
- isActive: PropTypes.bool.isRequired,
- trappedElId: PropTypes.string.isRequired,
- initialFocusRef: PropTypes.shape({
- current: PropTypes.object
- })
+ children: PropTypes.node.isRequired,
+ isActive: PropTypes.bool.isRequired,
+ trappedElId: PropTypes.string.isRequired,
+ initialFocusRef: PropTypes.shape({
+ current: PropTypes.object
+ })
};
export default FocusTrapper;
diff --git a/client/components/common/LoadingStatus/LoadingStatus.js b/client/components/common/LoadingStatus/LoadingStatus.js
index c5da4f084..55af3d9df 100644
--- a/client/components/common/LoadingStatus/LoadingStatus.js
+++ b/client/components/common/LoadingStatus/LoadingStatus.js
@@ -5,52 +5,52 @@ import styled from '@emotion/styled';
import BasicModal from '../../common/BasicModal';
const Container = styled.div`
- display: flex;
- justify-content: center;
+ display: flex;
+ justify-content: center;
`;
const LoadingStatus = ({ message, children }) => {
- const [isLoading, setIsLoading] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
- useEffect(() => {
- if (message) setIsLoading(true);
- else setIsLoading(false);
- }, [message]);
+ useEffect(() => {
+ if (message) setIsLoading(true);
+ else setIsLoading(false);
+ }, [message]);
- return (
- <>
- {isLoading && (
-
-
-
- }
- />
- )}
- {children}
- >
- );
+ return (
+ <>
+ {isLoading && (
+
+
+
+ }
+ />
+ )}
+ {children}
+ >
+ );
};
LoadingStatus.propTypes = {
- message: PropTypes.string,
- children: PropTypes.node
+ message: PropTypes.string,
+ children: PropTypes.node
};
export default LoadingStatus;
diff --git a/client/components/common/LoadingStatus/useTriggerLoad.js b/client/components/common/LoadingStatus/useTriggerLoad.js
index 55e4889a2..8301b3d15 100644
--- a/client/components/common/LoadingStatus/useTriggerLoad.js
+++ b/client/components/common/LoadingStatus/useTriggerLoad.js
@@ -1,22 +1,22 @@
import { useState } from 'react';
export function useTriggerLoad() {
- const [loadingMessage, setLoadingMessage] = useState('');
+ const [loadingMessage, setLoadingMessage] = useState('');
- const triggerLoad = (loadFn, message) => {
- return new Promise((resolve, reject) => {
- setLoadingMessage(message);
- loadFn()
- .then(() => {
- setLoadingMessage('');
- resolve();
- })
- .catch(e => {
- setLoadingMessage('');
- reject(e);
- });
+ const triggerLoad = (loadFn, message) => {
+ return new Promise((resolve, reject) => {
+ setLoadingMessage(message);
+ loadFn()
+ .then(() => {
+ setLoadingMessage('');
+ resolve();
+ })
+ .catch(e => {
+ setLoadingMessage('');
+ reject(e);
});
- };
+ });
+ };
- return { triggerLoad, loadingMessage };
+ return { triggerLoad, loadingMessage };
}
diff --git a/client/components/common/PageStatus/PageStatus.css b/client/components/common/PageStatus/PageStatus.css
index f4dca95fe..59449a4e1 100644
--- a/client/components/common/PageStatus/PageStatus.css
+++ b/client/components/common/PageStatus/PageStatus.css
@@ -1,11 +1,11 @@
.loading {
- display: inline-block;
- clip-path: inset(0 1ch 0 0);
- animation: dots 1s steps(4) infinite;
+ display: inline-block;
+ clip-path: inset(0 1ch 0 0);
+ animation: dots 1s steps(4) infinite;
}
@keyframes dots {
- to {
- clip-path: inset(0 -1ch 0 0);
- }
+ to {
+ clip-path: inset(0 -1ch 0 0);
+ }
}
diff --git a/client/components/common/PageStatus/index.jsx b/client/components/common/PageStatus/index.jsx
index 6886633ce..066dfd9f2 100644
--- a/client/components/common/PageStatus/index.jsx
+++ b/client/components/common/PageStatus/index.jsx
@@ -5,37 +5,37 @@ import { Helmet } from 'react-helmet';
import './PageStatus.css';
const Loading = ({
- title,
- heading,
- message = 'Loading ...',
- isError = false
+ title,
+ heading,
+ message = 'Loading ...',
+ isError = false
}) => {
- let className = isError ? 'alert alert-danger' : '';
- className = message === 'Loading ...' ? `${className} loading` : className;
+ let className = isError ? 'alert alert-danger' : '';
+ className = message === 'Loading ...' ? `${className} loading` : className;
- return (
-
-
- {title}
-
- {heading}
+ return (
+
+
+ {title}
+
+ {heading}
-
- {message}
-
-
- );
+
+ {message}
+
+
+ );
};
Loading.propTypes = {
- title: PropTypes.string,
- heading: PropTypes.string,
- message: PropTypes.string,
- isError: PropTypes.bool
+ title: PropTypes.string,
+ heading: PropTypes.string,
+ message: PropTypes.string,
+ isError: PropTypes.bool
};
export default Loading;
diff --git a/client/components/common/PhasePill/index.jsx b/client/components/common/PhasePill/index.jsx
index 992dce3b8..15ee3ca22 100644
--- a/client/components/common/PhasePill/index.jsx
+++ b/client/components/common/PhasePill/index.jsx
@@ -4,63 +4,63 @@ import { derivePhaseName } from '@client/utils/aria';
import styled from '@emotion/styled';
const PhaseText = styled.span`
- display: inline-block;
- padding: 2px 4px;
- border-radius: 14px;
+ display: inline-block;
+ padding: 2px 4px;
+ border-radius: 14px;
- text-align: center;
- overflow: hidden;
- white-space: nowrap;
- color: white;
+ text-align: center;
+ overflow: hidden;
+ white-space: nowrap;
+ color: white;
- &.full-width {
- width: 100%;
- }
- &:not(.full-width) {
- width: min-content;
- padding: 2px 15px;
- vertical-align: middle;
- position: relative;
- top: -4px;
- margin-top: 4px; /* Improve appearance when text wraps */
- }
+ &.full-width {
+ width: 100%;
+ }
+ &:not(.full-width) {
+ width: min-content;
+ padding: 2px 15px;
+ vertical-align: middle;
+ position: relative;
+ top: -4px;
+ margin-top: 4px; /* Improve appearance when text wraps */
+ }
- &.rd {
- background: #4177de;
- }
+ &.rd {
+ background: #4177de;
+ }
- &.draft {
- background: #818f98;
- }
+ &.draft {
+ background: #818f98;
+ }
- &.candidate {
- background: #ff6c00;
- }
+ &.candidate {
+ background: #ff6c00;
+ }
- &.recommended {
- background: #8441de;
- }
+ &.recommended {
+ background: #8441de;
+ }
- &.deprecated {
- background: #ce1b4c;
- }
+ &.deprecated {
+ background: #ce1b4c;
+ }
`;
const PhasePill = ({ fullWidth = true, children: phase }) => {
- return (
- str)
- .join(' ')}
- >
- {derivePhaseName(phase)}
-
- );
+ return (
+ str)
+ .join(' ')}
+ >
+ {derivePhaseName(phase)}
+
+ );
};
PhasePill.propTypes = {
- fullWidth: PropTypes.bool,
- children: PropTypes.string.isRequired
+ fullWidth: PropTypes.bool,
+ children: PropTypes.string.isRequired
};
export default PhasePill;
diff --git a/client/components/common/RadioBox/index.jsx b/client/components/common/RadioBox/index.jsx
index b1f8f1296..ff9057646 100644
--- a/client/components/common/RadioBox/index.jsx
+++ b/client/components/common/RadioBox/index.jsx
@@ -3,85 +3,85 @@ import PropTypes from 'prop-types';
import styled from '@emotion/styled';
const ContainerDiv = styled.div`
- display: flex;
+ display: flex;
`;
const Label = styled.label`
- border: 1px solid #ced4da;
- padding: 0.375rem 0.9rem 0.375rem 0.75rem;
- background-color: ${props =>
- props.applyCheckedStyles ? `#F6F8FA` : 'white'};
+ border: 1px solid #ced4da;
+ padding: 0.375rem 0.9rem 0.375rem 0.75rem;
+ background-color: ${props =>
+ props.applyCheckedStyles ? `#F6F8FA` : 'white'};
- &:first-of-type {
- border-radius: 0.375rem 0 0 0.375rem;
- }
- &:last-of-type {
- border-radius: 0 0.375rem 0.375rem 0;
- }
- &:not(:last-of-type) {
- border-right: none;
- }
+ &:first-of-type {
+ border-radius: 0.375rem 0 0 0.375rem;
+ }
+ &:last-of-type {
+ border-radius: 0 0.375rem 0.375rem 0;
+ }
+ &:not(:last-of-type) {
+ border-right: none;
+ }
`;
const Input = styled.input`
- margin-right: 0.375rem;
+ margin-right: 0.375rem;
`;
const RadioBox = ({ name, labels, selectedLabel, onSelect }) => {
- const getOnChange = label => event => {
- if (event.target.checked) onSelect(label);
- };
+ const getOnChange = label => event => {
+ if (event.target.checked) onSelect(label);
+ };
- return (
-
- {labels.map(label => {
- const isChecked = selectedLabel === label;
- return (
-
-
-
- {label}
-
-
- );
- })}
-
- );
+ return (
+
+ {labels.map(label => {
+ const isChecked = selectedLabel === label;
+ return (
+
+
+
+ {label}
+
+
+ );
+ })}
+
+ );
};
const TextThatWontShiftWhenBold = ({ isBold, children: text }) => {
- return (
-
-
- {text}
-
-
- {text}
-
-
- );
+ return (
+
+
+ {text}
+
+
+ {text}
+
+
+ );
};
TextThatWontShiftWhenBold.propTypes = {
- isBold: PropTypes.bool.isRequired,
- children: PropTypes.string.isRequired
+ isBold: PropTypes.bool.isRequired,
+ children: PropTypes.string.isRequired
};
RadioBox.propTypes = {
- name: PropTypes.string.isRequired,
- labels: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
- selectedLabel: PropTypes.string,
- onSelect: PropTypes.func.isRequired
+ name: PropTypes.string.isRequired,
+ labels: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
+ selectedLabel: PropTypes.string,
+ onSelect: PropTypes.func.isRequired
};
export default RadioBox;
diff --git a/client/components/common/ReportStatusDot/index.jsx b/client/components/common/ReportStatusDot/index.jsx
index 0c44c6521..e43bf78b6 100644
--- a/client/components/common/ReportStatusDot/index.jsx
+++ b/client/components/common/ReportStatusDot/index.jsx
@@ -1,47 +1,47 @@
import styled from '@emotion/styled';
const ReportStatusDot = styled.span`
- display: inline-block;
- height: 10px;
- width: 10px;
- padding: 0;
- margin-right: 8px;
- border-radius: 50%;
-
- &.issues {
- background: #f2ba00;
- }
-
- &.tests-skipped,
- &.reports-not-started {
- background: #7c7c7c;
- }
-
- &.tests-running {
- border: 2px solid #1e8f37;
- background: #d2d5d9;
- }
-
- &.tests-error {
- background: #e3261f;
- }
-
- &.tests-queued,
- &.reports-in-progress {
- background: #3876e8;
- }
-
- &.tests-complete,
- &.reports-complete {
- background: #2ba51c;
- }
-
- &.tests-cancelled {
- background: #a231ff;
- }
- &.reports-missing {
- background: #ce1b4c;
- }
+ display: inline-block;
+ height: 10px;
+ width: 10px;
+ padding: 0;
+ margin-right: 8px;
+ border-radius: 50%;
+
+ &.issues {
+ background: #f2ba00;
+ }
+
+ &.tests-skipped,
+ &.reports-not-started {
+ background: #7c7c7c;
+ }
+
+ &.tests-running {
+ border: 2px solid #1e8f37;
+ background: #d2d5d9;
+ }
+
+ &.tests-error {
+ background: #e3261f;
+ }
+
+ &.tests-queued,
+ &.reports-in-progress {
+ background: #3876e8;
+ }
+
+ &.tests-complete,
+ &.reports-complete {
+ background: #2ba51c;
+ }
+
+ &.tests-cancelled {
+ background: #a231ff;
+ }
+ &.reports-missing {
+ background: #ce1b4c;
+ }
`;
export default ReportStatusDot;
diff --git a/client/components/common/ReportStatusSummary/index.jsx b/client/components/common/ReportStatusSummary/index.jsx
index 904f81b9e..6e1ebf29e 100644
--- a/client/components/common/ReportStatusSummary/index.jsx
+++ b/client/components/common/ReportStatusSummary/index.jsx
@@ -5,93 +5,91 @@ import { convertDateToString } from '../../../utils/formatter';
import { calculatePercentComplete } from '../../../utils/calculatePercentComplete';
const IncompleteStatusReport = styled.span`
- min-width: 5rem;
- display: inline-block;
+ min-width: 5rem;
+ display: inline-block;
`;
const ReportStatusSummary = ({
- testPlanVersion,
- testPlanReport,
- fromTestQueue = false
+ testPlanVersion,
+ testPlanReport,
+ fromTestQueue = false
}) => {
- const renderCompleteReportStatus = testPlanReport => {
- const formattedDate = convertDateToString(
- testPlanReport.markedFinalAt,
- 'MMM D, YYYY'
+ const renderCompleteReportStatus = testPlanReport => {
+ const formattedDate = convertDateToString(
+ testPlanReport.markedFinalAt,
+ 'MMM D, YYYY'
+ );
+ return (
+
+ Report completed on {formattedDate}
+
+ );
+ };
+
+ const renderPartialCompleteReportStatus = testPlanReport => {
+ const { metrics, draftTestPlanRuns } = testPlanReport;
+
+ const conflictsCount = metrics.conflictsCount ?? 0;
+ const percentComplete = calculatePercentComplete(testPlanReport);
+ switch (draftTestPlanRuns?.length) {
+ case 0:
+ return fromTestQueue ? (
+ No testers assigned
+ ) : (
+ In test queue with no testers assigned
);
+ case 1:
return (
+
+ {percentComplete}% complete by
- Report completed on {formattedDate}
+ {draftTestPlanRuns[0].tester.username}
+ with {conflictsCount} conflicts
+
);
- };
-
- const renderPartialCompleteReportStatus = testPlanReport => {
- const { metrics, draftTestPlanRuns } = testPlanReport;
-
- const conflictsCount = metrics.conflictsCount ?? 0;
- const percentComplete = calculatePercentComplete(testPlanReport);
- switch (draftTestPlanRuns?.length) {
- case 0:
- return fromTestQueue ? (
- No testers assigned
- ) : (
- In test queue with no testers assigned
- );
- case 1:
- return (
-
- {percentComplete}% complete by
-
- {draftTestPlanRuns[0].tester.username}
-
- with {conflictsCount} conflicts
-
- );
- default:
- return (
-
- {percentComplete}% complete by
- {draftTestPlanRuns.length} testers with {conflictsCount}
- conflicts
-
- );
- }
- };
+ default:
+ return (
+
+ {percentComplete}% complete by
+ {draftTestPlanRuns.length} testers with {conflictsCount}
+ conflicts
+
+ );
+ }
+ };
- if (testPlanReport) {
- const { markedFinalAt } = testPlanReport;
- if (markedFinalAt) {
- return renderCompleteReportStatus(testPlanReport);
- } else {
- return renderPartialCompleteReportStatus(testPlanReport);
- }
+ if (testPlanReport) {
+ const { markedFinalAt } = testPlanReport;
+ if (markedFinalAt) {
+ return renderCompleteReportStatus(testPlanReport);
+ } else {
+ return renderPartialCompleteReportStatus(testPlanReport);
}
+ }
- return Missing ;
+ return Missing ;
};
ReportStatusSummary.propTypes = {
- testPlanVersion: PropTypes.shape({
- id: PropTypes.string.isRequired
- }).isRequired,
- testPlanReport: PropTypes.shape({
- id: PropTypes.string.isRequired,
- markedFinalAt: PropTypes.string,
- metrics: PropTypes.object,
- draftTestPlanRuns: PropTypes.arrayOf(
- PropTypes.shape({
- tester: PropTypes.shape({
- username: PropTypes.string.isRequired
- }).isRequired
- })
- ).isRequired
- }),
- fromTestQueue: PropTypes.bool
+ testPlanVersion: PropTypes.shape({
+ id: PropTypes.string.isRequired
+ }).isRequired,
+ testPlanReport: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ markedFinalAt: PropTypes.string,
+ metrics: PropTypes.object,
+ draftTestPlanRuns: PropTypes.arrayOf(
+ PropTypes.shape({
+ tester: PropTypes.shape({
+ username: PropTypes.string.isRequired
+ }).isRequired
+ })
+ ).isRequired
+ }),
+ fromTestQueue: PropTypes.bool
};
export default ReportStatusSummary;
diff --git a/client/components/common/SortableTableHeader/index.js b/client/components/common/SortableTableHeader/index.js
index b274d3079..c1fb828a2 100644
--- a/client/components/common/SortableTableHeader/index.js
+++ b/client/components/common/SortableTableHeader/index.js
@@ -4,141 +4,141 @@ import { Button } from 'react-bootstrap';
import styled from '@emotion/styled';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
- faArrowDownShortWide,
- faArrowUpShortWide
+ faArrowDownShortWide,
+ faArrowUpShortWide
} from '@fortawesome/free-solid-svg-icons';
import { useAriaLiveRegion } from '../../providers/AriaLiveRegionProvider';
const SortableTableHeaderWrapper = styled.th`
- position: relative;
- padding: 0;
+ position: relative;
+ padding: 0;
`;
const SortableTableHeaderButton = styled(Button)`
+ background: #e9ebee;
+ border: none;
+ color: black;
+ font-size: 1rem;
+ padding: 0;
+ font-weight: 700;
+ text-align: left;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ justify-content: space-between;
+ align-items: flex-end;
+ padding: 8px 12px;
+ border-radius: 0;
+ z-index: 0;
+ display: flex;
+ &:hover,
+ &:focus {
background: #e9ebee;
- border: none;
- color: black;
- font-size: 1rem;
- padding: 0;
- font-weight: 700;
- text-align: left;
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- justify-content: space-between;
- align-items: flex-end;
- padding: 8px 12px;
- border-radius: 0;
- z-index: 0;
- display: flex;
- &:hover,
- &:focus {
- background: #e9ebee;
- z-index: 1;
- color: #0b60ab;
- background-color: var(--bs-table-hover-bg);
- }
+ z-index: 1;
+ color: #0b60ab;
+ background-color: var(--bs-table-hover-bg);
+ }
- &:hover {
- border: none;
- }
+ &:hover {
+ border: none;
+ }
`;
const InactiveIcon = styled(FontAwesomeIcon)`
- color: rgb(155, 155, 155);
+ color: rgb(155, 155, 155);
`;
export const TABLE_SORT_ORDERS = {
- ASC: 'ASCENDING',
- DESC: 'DESCENDING'
+ ASC: 'ASCENDING',
+ DESC: 'DESCENDING'
};
const SortableTableHeader = ({
- title,
- active,
- onSort = () => {},
- initialSortDirection = TABLE_SORT_ORDERS.ASC
+ title,
+ active,
+ onSort = () => {},
+ initialSortDirection = TABLE_SORT_ORDERS.ASC
}) => {
- const [currentSortOrder, setCurrentSortOrder] =
- useState(initialSortDirection);
-
- const setAlertMessage = useAriaLiveRegion();
-
- useEffect(() => {
- if (active) {
- const message =
- `Test Plans Status Summary Table, now sorted by ${title} in ` +
- `${currentSortOrder.toLowerCase()} order`;
- setAlertMessage(message);
- }
- }, [active, currentSortOrder, setAlertMessage, title]);
-
- const handleClick = () => {
- if (active) {
- const newSortOrder =
- currentSortOrder === TABLE_SORT_ORDERS.ASC
- ? TABLE_SORT_ORDERS.DESC
- : TABLE_SORT_ORDERS.ASC;
- setCurrentSortOrder(newSortOrder);
- onSort(newSortOrder);
- } else {
- onSort(currentSortOrder);
- }
- };
+ const [currentSortOrder, setCurrentSortOrder] =
+ useState(initialSortDirection);
- const getIcon = () => {
- const icon =
- currentSortOrder === TABLE_SORT_ORDERS.ASC
- ? faArrowUpShortWide
- : faArrowDownShortWide;
-
- const attribs = {
- 'aria-hidden': 'true',
- focusable: 'false',
- icon: icon
- };
-
- if (active) {
- return ;
- } else {
- return ;
- }
- };
+ const setAlertMessage = useAriaLiveRegion();
- const getAriaSort = () => {
- if (!active) {
- return 'none';
- } else {
- return currentSortOrder === TABLE_SORT_ORDERS.ASC
- ? 'ascending'
- : 'descending';
- }
+ useEffect(() => {
+ if (active) {
+ const message =
+ `Test Plans Status Summary Table, now sorted by ${title} in ` +
+ `${currentSortOrder.toLowerCase()} order`;
+ setAlertMessage(message);
+ }
+ }, [active, currentSortOrder, setAlertMessage, title]);
+
+ const handleClick = () => {
+ if (active) {
+ const newSortOrder =
+ currentSortOrder === TABLE_SORT_ORDERS.ASC
+ ? TABLE_SORT_ORDERS.DESC
+ : TABLE_SORT_ORDERS.ASC;
+ setCurrentSortOrder(newSortOrder);
+ onSort(newSortOrder);
+ } else {
+ onSort(currentSortOrder);
+ }
+ };
+
+ const getIcon = () => {
+ const icon =
+ currentSortOrder === TABLE_SORT_ORDERS.ASC
+ ? faArrowUpShortWide
+ : faArrowDownShortWide;
+
+ const attribs = {
+ 'aria-hidden': 'true',
+ focusable: 'false',
+ icon: icon
};
- return (
-
-
- {title}
- {getIcon()}
-
-
- );
+ if (active) {
+ return ;
+ } else {
+ return ;
+ }
+ };
+
+ const getAriaSort = () => {
+ if (!active) {
+ return 'none';
+ } else {
+ return currentSortOrder === TABLE_SORT_ORDERS.ASC
+ ? 'ascending'
+ : 'descending';
+ }
+ };
+
+ return (
+
+
+ {title}
+ {getIcon()}
+
+
+ );
};
SortableTableHeader.propTypes = {
- title: PropTypes.string.isRequired,
- active: PropTypes.bool.isRequired,
- onSort: PropTypes.func.isRequired,
- initialSortDirection: PropTypes.oneOf([
- TABLE_SORT_ORDERS.ASC,
- TABLE_SORT_ORDERS.DESC
- ])
+ title: PropTypes.string.isRequired,
+ active: PropTypes.bool.isRequired,
+ onSort: PropTypes.func.isRequired,
+ initialSortDirection: PropTypes.oneOf([
+ TABLE_SORT_ORDERS.ASC,
+ TABLE_SORT_ORDERS.DESC
+ ])
};
export default SortableTableHeader;
diff --git a/client/components/common/TestPlanResultsTable/TestPlanResultsTable.css b/client/components/common/TestPlanResultsTable/TestPlanResultsTable.css
index 31b97fd63..a217a3beb 100644
--- a/client/components/common/TestPlanResultsTable/TestPlanResultsTable.css
+++ b/client/components/common/TestPlanResultsTable/TestPlanResultsTable.css
@@ -1,24 +1,24 @@
table.test-plan-results-table {
- /* To override `caption-side: bottom;` coming from _reboot.scss */
- caption-side: unset;
+ /* To override `caption-side: bottom;` coming from _reboot.scss */
+ caption-side: unset;
}
table.test-plan-unexpected-behaviors-table {
- margin-top: 0.5rem;
+ margin-top: 0.5rem;
}
p.test-plan-results-response-p {
- margin: 0;
+ margin: 0;
}
blockquote.test-plan-results-blockquote {
- background: #f7f7f7;
- border-left: 4px solid #d3d5d9;
- margin: 0.5em 0;
- padding: 0.5em;
+ background: #f7f7f7;
+ border-left: 4px solid #d3d5d9;
+ margin: 0.5em 0;
+ padding: 0.5em;
}
li.test-plan-results-li {
- margin-left: 1rem;
- list-style: disc;
+ margin-left: 1rem;
+ list-style: disc;
}
diff --git a/client/components/common/TestPlanResultsTable/index.jsx b/client/components/common/TestPlanResultsTable/index.jsx
index 30dccaa36..c29bcb688 100644
--- a/client/components/common/TestPlanResultsTable/index.jsx
+++ b/client/components/common/TestPlanResultsTable/index.jsx
@@ -5,207 +5,204 @@ import nextId from 'react-id-generator';
import { getMetrics } from 'shared';
import './TestPlanResultsTable.css';
+const getAssertionResultText = (passed, priority) => {
+ if (priority === 'MAY') {
+ return passed ? 'Supported' : 'Unsupported';
+ }
+ return passed ? 'Passed' : 'Failed';
+};
+
const renderAssertionRow = (assertionResult, priorityString) => {
- return (
-
- {priorityString}
-
- {assertionResult.assertion.phrase
- ? assertionResult.assertion.phrase.charAt(0).toUpperCase() +
- assertionResult.assertion.phrase.slice(1)
- : assertionResult.assertion.text.charAt(0).toUpperCase() +
- assertionResult.assertion.text.slice(1)}
-
- {assertionResult.passed ? 'Passed' : 'Failed'}
-
- );
+ return (
+
+ {priorityString}
+
+ {assertionResult.assertion.phrase
+ ? assertionResult.assertion.phrase.charAt(0).toUpperCase() +
+ assertionResult.assertion.phrase.slice(1)
+ : assertionResult.assertion.text.charAt(0).toUpperCase() +
+ assertionResult.assertion.text.slice(1)}
+
+ {getAssertionResultText(assertionResult.passed, priorityString)}
+
+ );
};
const TestPlanResultsTable = ({
- test,
- testResult,
- tableClassName = '',
- optionalHeader = null,
- commandHeadingLevel = 3
+ test,
+ testResult,
+ tableClassName = '',
+ optionalHeader = null,
+ commandHeadingLevel = 3
}) => {
- const CommandHeading = `h${commandHeadingLevel}`;
+ const CommandHeading = `h${commandHeadingLevel}`;
- return (
- <>
- {optionalHeader}
- {testResult.scenarioResults.map((scenarioResult, index) => {
- const {
- assertionsPassedCount,
- assertionsFailedCount,
- severeImpactPassedAssertionCount,
- moderateImpactPassedAssertionCount
- } = getMetrics({ scenarioResult });
+ return (
+ <>
+ {optionalHeader}
+ {testResult.scenarioResults.map((scenarioResult, index) => {
+ const {
+ assertionsPassedCount,
+ assertionsFailedCount,
+ severeImpactPassedAssertionCount,
+ moderateImpactPassedAssertionCount
+ } = getMetrics({ scenarioResult });
- const hasNoSevereUnexpectedBehavior =
- severeImpactPassedAssertionCount > 0;
- const hasNoModerateUnexpectedBehavior =
- moderateImpactPassedAssertionCount > 0;
+ const hasNoSevereUnexpectedBehavior =
+ severeImpactPassedAssertionCount > 0;
+ const hasNoModerateUnexpectedBehavior =
+ moderateImpactPassedAssertionCount > 0;
- // Rows are sorted by priority descending, then result (failures then passes), then
- // assertion order. Assertion order refers to the order of assertion columns in the
- // tests.csv file.
- const mustAssertionResults = scenarioResult.mustAssertionResults
- .slice()
- .sort((a, b) =>
- a.passed === b.passed ? 0 : a.passed ? 1 : -1
- );
+ // Rows are sorted by priority descending, then result (failures then passes), then
+ // assertion order. Assertion order refers to the order of assertion columns in the
+ // tests.csv file.
+ const mustAssertionResults = scenarioResult.mustAssertionResults
+ .slice()
+ .sort((a, b) => (a.passed === b.passed ? 0 : a.passed ? 1 : -1));
- const shouldAssertionResults =
- scenarioResult.shouldAssertionResults
- .slice()
- .sort((a, b) =>
- a.passed === b.passed ? 0 : a.passed ? 1 : -1
- );
+ const shouldAssertionResults = scenarioResult.shouldAssertionResults
+ .slice()
+ .sort((a, b) => (a.passed === b.passed ? 0 : a.passed ? 1 : -1));
- const mayAssertionResults = scenarioResult.mayAssertionResults
- .slice()
- .sort((a, b) =>
- a.passed === b.passed ? 0 : a.passed ? 1 : -1
- );
+ const mayAssertionResults = scenarioResult.mayAssertionResults
+ .slice()
+ .sort((a, b) => (a.passed === b.passed ? 0 : a.passed ? 1 : -1));
- // Workaround:
- // Remove instances of content inside '()' to address edge case of
- // COMMAND_TEXT (OPERATING_MODE) then COMMAND_TEXT (OPERATING_MODE).
- // OPERATING_MODE should only show once
- const bracketsRegex = /\((.*?)\)/g;
+ // Workaround:
+ // Remove instances of content inside '()' to address edge case of
+ // COMMAND_TEXT (OPERATING_MODE) then COMMAND_TEXT (OPERATING_MODE).
+ // OPERATING_MODE should only show once
+ const bracketsRegex = /\((.*?)\)/g;
- const commandsString = scenarioResult.scenario.commands
- .map(({ text }, index) => {
- if (
- index !==
- scenarioResult.scenario.commands.length - 1
- )
- text = text.replace(bracketsRegex, '');
- return text.trim();
- })
- .join(' then ');
+ const commandsString = scenarioResult.scenario.commands
+ .map(({ text }, index) => {
+ if (index !== scenarioResult.scenario.commands.length - 1)
+ text = text.replace(bracketsRegex, '');
+ return text.trim();
+ })
+ .join(' then ');
- const sortedAssertionResults = [
- ...mustAssertionResults.map(e => ({
- ...e,
- priorityString: 'MUST'
- })),
- {
- id: `UnexpectedBehavior_MUST_${nextId()}`,
- assertion: {
- text: 'Other behaviors that create severe negative impacts are not exhibited'
- },
- passed: hasNoSevereUnexpectedBehavior,
- priorityString: 'MUST'
- },
- ...shouldAssertionResults.map(e => ({
- ...e,
- priorityString: 'SHOULD'
- })),
- {
- id: `UnexpectedBehavior_SHOULD_${nextId()}`,
- assertion: {
- text: 'Other behaviors that create moderate negative impacts are not exhibited'
- },
- passed: hasNoModerateUnexpectedBehavior,
- priorityString: 'SHOULD'
- },
- ...mayAssertionResults.map(e => ({
- ...e,
- priorityString: 'MAY'
- }))
- ].sort((a, b) => a.passed - b.passed);
+ const sortedAssertionResults = [
+ ...mustAssertionResults.map(e => ({
+ ...e,
+ priorityString: 'MUST'
+ })),
+ {
+ id: `UnexpectedBehavior_MUST_${nextId()}`,
+ assertion: {
+ text: 'Other behaviors that create severe negative impacts are not exhibited'
+ },
+ passed: hasNoSevereUnexpectedBehavior,
+ priorityString: 'MUST'
+ },
+ ...shouldAssertionResults.map(e => ({
+ ...e,
+ priorityString: 'SHOULD'
+ })),
+ {
+ id: `UnexpectedBehavior_SHOULD_${nextId()}`,
+ assertion: {
+ text: 'Other behaviors that create moderate negative impacts are not exhibited'
+ },
+ passed: hasNoModerateUnexpectedBehavior,
+ priorityString: 'SHOULD'
+ },
+ ...mayAssertionResults.map(e => ({
+ ...e,
+ priorityString: 'MAY'
+ }))
+ ].sort((a, b) => a.passed - b.passed);
- return (
-
-
- {commandsString} Results:
- {assertionsPassedCount} passed,
- {assertionsFailedCount} failed
-
-
- {test.at?.name} Response:
-
-
- {scenarioResult.output}
-
-
- {commandsString} Results
-
-
- Priority
- Assertion
- Result
-
-
-
- {sortedAssertionResults.map(assertionResult =>
- renderAssertionRow(
- assertionResult,
- assertionResult.priorityString
- )
- )}
-
-
- Other behaviors that create negative impact:{' '}
- {scenarioResult.unexpectedBehaviors.length ? (
-
-
-
- Behavior
- Details
- Impact
-
-
-
- {scenarioResult.unexpectedBehaviors.map(
- ({ id, text, details, impact }) => (
-
- {text}
- {details}
- {impact}
-
- )
- )}
-
-
- ) : (
- 'None'
- )}
- {/* Do not show separator below last item */}
- {index !== testResult.scenarioResults.length - 1 ? (
-
- ) : null}
-
- );
- })}
- >
- );
+ return (
+
+
+ {commandsString} Results:
+ {assertionsPassedCount} passed,
+ {assertionsFailedCount} failed
+
+
+ {test.at?.name} Response:
+
+
+ {scenarioResult.output}
+
+
+ {commandsString} Results
+
+
+ Priority
+ Assertion
+ Result
+
+
+
+ {sortedAssertionResults.map(assertionResult =>
+ renderAssertionRow(
+ assertionResult,
+ assertionResult.priorityString
+ )
+ )}
+
+
+ Other behaviors that create negative impact:{' '}
+ {scenarioResult.unexpectedBehaviors.length ? (
+
+
+
+ Behavior
+ Details
+ Impact
+
+
+
+ {scenarioResult.unexpectedBehaviors.map(
+ ({ id, text, details, impact }) => (
+
+ {text}
+ {details}
+ {impact}
+
+ )
+ )}
+
+
+ ) : (
+ 'None'
+ )}
+ {/* Do not show separator below last item */}
+ {index !== testResult.scenarioResults.length - 1 ? (
+
+ ) : null}
+
+ );
+ })}
+ >
+ );
};
TestPlanResultsTable.propTypes = {
- test: PropTypes.shape({
- title: PropTypes.string.isRequired,
- at: PropTypes.shape({
- name: PropTypes.string.isRequired
- }).isRequired
- }),
- testResult: PropTypes.shape({
- scenarioResults: PropTypes.array.isRequired
- }),
- tableClassName: PropTypes.string,
- optionalHeader: PropTypes.node,
- commandHeadingLevel: PropTypes.number
+ test: PropTypes.shape({
+ title: PropTypes.string.isRequired,
+ at: PropTypes.shape({
+ name: PropTypes.string.isRequired
+ }).isRequired
+ }),
+ testResult: PropTypes.shape({
+ scenarioResults: PropTypes.array.isRequired
+ }),
+ tableClassName: PropTypes.string,
+ optionalHeader: PropTypes.node,
+ commandHeadingLevel: PropTypes.number
};
export default TestPlanResultsTable;
diff --git a/client/components/common/ThemeTable/index.jsx b/client/components/common/ThemeTable/index.jsx
index 12d2e739c..aef2a86e1 100644
--- a/client/components/common/ThemeTable/index.jsx
+++ b/client/components/common/ThemeTable/index.jsx
@@ -2,37 +2,37 @@ import { Table } from 'react-bootstrap';
import styled from '@emotion/styled';
export const ThemeTableHeaderH2 = styled.h2`
- background-color: var(--bs-table-bg) !important;
- font-size: 1.5rem;
- font-weight: 600;
- border: solid 1px #d2d5d9;
- border-bottom: none;
- padding: 0.5rem 1rem;
- margin: 0.5rem 0 0 0;
+ background-color: var(--bs-table-bg) !important;
+ font-size: 1.5rem;
+ font-weight: 600;
+ border: solid 1px #d2d5d9;
+ border-bottom: none;
+ padding: 0.5rem 1rem;
+ margin: 0.5rem 0 0 0;
`;
export const ThemeTableHeaderH3 = styled.h3`
- background-color: var(--bs-table-bg) !important;
- font-size: 1.25rem;
- font-weight: 600;
- border: solid 1px #d2d5d9;
- border-bottom: none;
- padding: 0.5rem 1rem;
- margin: 0.5rem 0 0 0;
+ background-color: var(--bs-table-bg) !important;
+ font-size: 1.25rem;
+ font-weight: 600;
+ border: solid 1px #d2d5d9;
+ border-bottom: none;
+ padding: 0.5rem 1rem;
+ margin: 0.5rem 0 0 0;
`;
export const ThemeTable = styled(Table)`
- margin-bottom: 0;
+ margin-bottom: 0;
- td,
- th {
- padding-left: 1rem;
- min-width: 165px;
- vertical-align: middle;
- }
+ td,
+ th {
+ padding-left: 1rem;
+ min-width: 165px;
+ vertical-align: middle;
+ }
`;
export const ThemeTableUnavailable = styled.div`
- border: solid 1px #d2d5d9;
- padding: 0.5rem 1rem;
+ border: solid 1px #d2d5d9;
+ padding: 0.5rem 1rem;
`;
diff --git a/client/components/common/UpdateTargetDateModal/index.jsx b/client/components/common/UpdateTargetDateModal/index.jsx
index 85105190e..17bc30929 100644
--- a/client/components/common/UpdateTargetDateModal/index.jsx
+++ b/client/components/common/UpdateTargetDateModal/index.jsx
@@ -6,138 +6,136 @@ import BasicModal from '../BasicModal';
import { convertDateToString, isValidDate } from '../../../utils/formatter';
const ModalInnerSectionContainer = styled.div`
- display: flex;
- flex-direction: column;
+ display: flex;
+ flex-direction: column;
`;
const UpdateTargetDateModal = ({
- show = false,
- title = null,
- dateText = '',
- handleAction = () => {},
- handleClose = () => {}
+ show = false,
+ title = null,
+ dateText = '',
+ handleAction = () => {},
+ handleClose = () => {}
}) => {
- const dateTextRef = useRef();
+ const dateTextRef = useRef();
- const [updatedDateText, setUpdatedDateText] = useState(
- convertDateToString(dateText)
- );
- const [isDateError, setIsDateError] = useState(false);
+ const [updatedDateText, setUpdatedDateText] = useState(
+ convertDateToString(dateText)
+ );
+ const [isDateError, setIsDateError] = useState(false);
- useEffect(() => {
- setUpdatedDateText(convertDateToString(dateText));
- }, [dateText]);
+ useEffect(() => {
+ setUpdatedDateText(convertDateToString(dateText));
+ }, [dateText]);
- const handleDateTextChange = e => {
- const value = e.target.value;
- setIsDateError(false);
- setUpdatedDateText(value);
- };
+ const handleDateTextChange = e => {
+ const value = e.target.value;
+ setIsDateError(false);
+ setUpdatedDateText(value);
+ };
- const handleDateTextKeyPress = e => {
- /**
- * Only accept the following ASCII characters:
- * 45: -
- * 48: 0
- * 49: 1
- * 50: 2
- * 51: 3
- * 52: 4
- * 53: 5
- * 54: 6
- * 55: 7
- * 56: 8
- * 57: 9
- */
- const HYPHEN = 45;
- const DIGIT_ZERO = 48;
- const DIGIT_NINE = 57;
- if (
- (e.charCode < DIGIT_ZERO && e.charCode !== HYPHEN) ||
- e.charCode > DIGIT_NINE
- ) {
- e.preventDefault();
- }
+ const handleDateTextKeyPress = e => {
+ /**
+ * Only accept the following ASCII characters:
+ * 45: -
+ * 48: 0
+ * 49: 1
+ * 50: 2
+ * 51: 3
+ * 52: 4
+ * 53: 5
+ * 54: 6
+ * 55: 7
+ * 56: 8
+ * 57: 9
+ */
+ const HYPHEN = 45;
+ const DIGIT_ZERO = 48;
+ const DIGIT_NINE = 57;
+ if (
+ (e.charCode < DIGIT_ZERO && e.charCode !== HYPHEN) ||
+ e.charCode > DIGIT_NINE
+ ) {
+ e.preventDefault();
+ }
- let input = e.target;
- let inputLength = input.value.length;
- if (inputLength !== 1 || inputLength !== 3) {
- if (e.charCode === HYPHEN) e.preventDefault();
- }
- if (inputLength === 2) input.value += '-';
- if (inputLength === 5) input.value += '-';
- };
+ let input = e.target;
+ let inputLength = input.value.length;
+ if (inputLength !== 1 || inputLength !== 3) {
+ if (e.charCode === HYPHEN) e.preventDefault();
+ }
+ if (inputLength === 2) input.value += '-';
+ if (inputLength === 5) input.value += '-';
+ };
- const onSubmit = () => {
- // Passed handleAction prop should account for dateText
- const dateTextError =
- !updatedDateText ||
- updatedDateText.length !== 10 ||
- !isValidDate(updatedDateText);
+ const onSubmit = () => {
+ // Passed handleAction prop should account for dateText
+ const dateTextError =
+ !updatedDateText ||
+ updatedDateText.length !== 10 ||
+ !isValidDate(updatedDateText);
- if (dateTextError) {
- setIsDateError(dateTextError);
- dateTextRef.current.focus();
- return;
- }
+ if (dateTextError) {
+ setIsDateError(dateTextError);
+ dateTextRef.current.focus();
+ return;
+ }
- handleAction({ updatedDateText });
- };
+ handleAction({ updatedDateText });
+ };
- return (
-
-
-
- Target Date
-
-
- {isDateError && (
-
- Please enter a valid date.
-
- )}
-
-
- }
- actions={[
- {
- label: 'Save',
- onClick: onSubmit
- }
- ]}
- handleClose={handleClose}
- useOnHide={true}
- handleHide={handleClose}
- initialFocusRef={dateTextRef}
- />
- );
+ return (
+
+
+ Target Date
+
+ {isDateError && (
+
+ Please enter a valid date.
+
+ )}
+
+
+ }
+ actions={[
+ {
+ label: 'Save',
+ onClick: onSubmit
+ }
+ ]}
+ handleClose={handleClose}
+ useOnHide={true}
+ handleHide={handleClose}
+ initialFocusRef={dateTextRef}
+ />
+ );
};
UpdateTargetDateModal.propTypes = {
- show: PropTypes.bool,
- title: PropTypes.node.isRequired,
- dateText: PropTypes.string,
- handleAction: PropTypes.func,
- handleClose: PropTypes.func
+ show: PropTypes.bool,
+ title: PropTypes.node.isRequired,
+ dateText: PropTypes.string,
+ handleAction: PropTypes.func,
+ handleClose: PropTypes.func
};
export default UpdateTargetDateModal;
diff --git a/client/components/common/UpdateVersionModal/index.jsx b/client/components/common/UpdateVersionModal/index.jsx
index ab64c2401..83a5daad9 100644
--- a/client/components/common/UpdateVersionModal/index.jsx
+++ b/client/components/common/UpdateVersionModal/index.jsx
@@ -6,178 +6,174 @@ import BasicModal from '../BasicModal';
import { convertDateToString, isValidDate } from '../../../utils/formatter';
const ModalInnerSectionContainer = styled.div`
- display: flex;
- flex-direction: column;
+ display: flex;
+ flex-direction: column;
`;
const UpdateVersionModal = ({
- show = false,
- title = null,
- actionType = 'add', // or edit
- versionText = '',
- dateAvailabilityText = '',
- handleAction = () => {},
- handleClose = () => {}
+ show = false,
+ title = null,
+ actionType = 'add', // or edit
+ versionText = '',
+ dateAvailabilityText = '',
+ handleAction = () => {},
+ handleClose = () => {}
}) => {
- const versionTextRef = useRef();
- const dateAvailabilityTextRef = useRef();
-
- const [updatedVersionText, setUpdatedVersionText] = useState(versionText);
- const [updatedDateAvailabilityText, setUpdatedDateAvailabilityText] =
- useState(convertDateToString(dateAvailabilityText));
- const [isVersionError, setIsVersionError] = useState(false);
- const [isDateError, setIsDateError] = useState(false);
-
- useEffect(() => {
- setUpdatedVersionText(versionText);
- setUpdatedDateAvailabilityText(
- convertDateToString(dateAvailabilityText)
- );
- }, [versionText, dateAvailabilityText]);
-
- const handleVersionTextChange = e => {
- const value = e.target.value;
- setIsVersionError(false);
- setUpdatedVersionText(value);
- };
-
- const handleDateAvailabilityTextChange = e => {
- const value = e.target.value;
- setIsDateError(false);
- setUpdatedDateAvailabilityText(value);
- };
-
- const handleDateAvailabilityTextKeyPress = e => {
- /**
- * Only accept the following ASCII characters:
- * 45: -
- * 48: 0
- * 49: 1
- * 50: 2
- * 51: 3
- * 52: 4
- * 53: 5
- * 54: 6
- * 55: 7
- * 56: 8
- * 57: 9
- */
- const HYPHEN = 45;
- const DIGIT_ZERO = 48;
- const DIGIT_NINE = 57;
- if (
- (e.charCode < DIGIT_ZERO && e.charCode !== HYPHEN) ||
- e.charCode > DIGIT_NINE
- ) {
- e.preventDefault();
+ const versionTextRef = useRef();
+ const dateAvailabilityTextRef = useRef();
+
+ const [updatedVersionText, setUpdatedVersionText] = useState(versionText);
+ const [updatedDateAvailabilityText, setUpdatedDateAvailabilityText] =
+ useState(convertDateToString(dateAvailabilityText));
+ const [isVersionError, setIsVersionError] = useState(false);
+ const [isDateError, setIsDateError] = useState(false);
+
+ useEffect(() => {
+ setUpdatedVersionText(versionText);
+ setUpdatedDateAvailabilityText(convertDateToString(dateAvailabilityText));
+ }, [versionText, dateAvailabilityText]);
+
+ const handleVersionTextChange = e => {
+ const value = e.target.value;
+ setIsVersionError(false);
+ setUpdatedVersionText(value);
+ };
+
+ const handleDateAvailabilityTextChange = e => {
+ const value = e.target.value;
+ setIsDateError(false);
+ setUpdatedDateAvailabilityText(value);
+ };
+
+ const handleDateAvailabilityTextKeyPress = e => {
+ /**
+ * Only accept the following ASCII characters:
+ * 45: -
+ * 48: 0
+ * 49: 1
+ * 50: 2
+ * 51: 3
+ * 52: 4
+ * 53: 5
+ * 54: 6
+ * 55: 7
+ * 56: 8
+ * 57: 9
+ */
+ const HYPHEN = 45;
+ const DIGIT_ZERO = 48;
+ const DIGIT_NINE = 57;
+ if (
+ (e.charCode < DIGIT_ZERO && e.charCode !== HYPHEN) ||
+ e.charCode > DIGIT_NINE
+ ) {
+ e.preventDefault();
+ }
+
+ let input = e.target;
+ let inputLength = input.value.length;
+ if (inputLength !== 1 || inputLength !== 3) {
+ if (e.charCode === HYPHEN) e.preventDefault();
+ }
+ if (inputLength === 2) input.value += '-';
+ if (inputLength === 5) input.value += '-';
+ };
+
+ const onSubmit = () => {
+ // Passed action prop should account for actionType, versionText and dateAvailabilityText
+ const versionTextError = !updatedVersionText;
+ const dateTextError =
+ !updatedDateAvailabilityText ||
+ updatedDateAvailabilityText.length !== 10 ||
+ !isValidDate(updatedDateAvailabilityText);
+
+ if (versionTextError || dateTextError) {
+ setIsVersionError(versionTextError);
+ versionTextRef.current.focus();
+
+ setIsDateError(dateTextError);
+ if (!versionTextError) dateAvailabilityTextRef.current.focus();
+ return;
+ }
+
+ handleAction(actionType, {
+ updatedVersionText,
+ updatedDateAvailabilityText
+ });
+ };
+
+ return (
+
+
+ Version Number
+
+ {isVersionError && (
+
+ Please enter a valid version number.
+
+ )}
+
+
+
+ Approximate date of availability
+
+ {isDateError && (
+
+ Please enter a valid date.
+
+ )}
+
+
+ }
+ actions={[
+ {
+ label: actionType === 'add' ? 'Add Version' : 'Save',
+ onClick: onSubmit
}
-
- let input = e.target;
- let inputLength = input.value.length;
- if (inputLength !== 1 || inputLength !== 3) {
- if (e.charCode === HYPHEN) e.preventDefault();
- }
- if (inputLength === 2) input.value += '-';
- if (inputLength === 5) input.value += '-';
- };
-
- const onSubmit = () => {
- // Passed action prop should account for actionType, versionText and dateAvailabilityText
- const versionTextError = !updatedVersionText;
- const dateTextError =
- !updatedDateAvailabilityText ||
- updatedDateAvailabilityText.length !== 10 ||
- !isValidDate(updatedDateAvailabilityText);
-
- if (versionTextError || dateTextError) {
- setIsVersionError(versionTextError);
- versionTextRef.current.focus();
-
- setIsDateError(dateTextError);
- if (!versionTextError) dateAvailabilityTextRef.current.focus();
- return;
- }
-
- handleAction(actionType, {
- updatedVersionText,
- updatedDateAvailabilityText
- });
- };
-
- return (
-
-
- Version Number
-
- {isVersionError && (
-
- Please enter a valid version number.
-
- )}
-
-
-
-
- Approximate date of availability
-
-
- {isDateError && (
-
- Please enter a valid date.
-
- )}
-
-
- }
- actions={[
- {
- label: actionType === 'add' ? 'Add Version' : 'Save',
- onClick: onSubmit
- }
- ]}
- handleClose={handleClose}
- />
- );
+ ]}
+ handleClose={handleClose}
+ />
+ );
};
UpdateVersionModal.propTypes = {
- show: PropTypes.bool,
- title: PropTypes.node.isRequired,
- actionType: PropTypes.string,
- versionText: PropTypes.string,
- dateAvailabilityText: PropTypes.string,
- onVersionChange: PropTypes.func,
- onDateAvailabilityChange: PropTypes.func,
- handleAction: PropTypes.func,
- handleClose: PropTypes.func
+ show: PropTypes.bool,
+ title: PropTypes.node.isRequired,
+ actionType: PropTypes.string,
+ versionText: PropTypes.string,
+ dateAvailabilityText: PropTypes.string,
+ onVersionChange: PropTypes.func,
+ onDateAvailabilityChange: PropTypes.func,
+ handleAction: PropTypes.func,
+ handleClose: PropTypes.func
};
export default UpdateVersionModal;
diff --git a/client/components/common/VersionString/index.js b/client/components/common/VersionString/index.js
index 5b74cd837..4045d67f0 100644
--- a/client/components/common/VersionString/index.js
+++ b/client/components/common/VersionString/index.js
@@ -5,93 +5,93 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import styled from '@emotion/styled';
const StyledPill = styled.span`
- display: inline-block;
+ display: inline-block;
- line-height: 2rem;
- border-radius: 4px;
+ line-height: 2rem;
+ border-radius: 4px;
- background: #f6f8fa;
- white-space: nowrap;
- text-align: center;
+ background: #f6f8fa;
+ white-space: nowrap;
+ text-align: center;
- // Needed for presenting component on Version History page
- &.full-width {
- width: 100%;
+ // Needed for presenting component on Version History page
+ &.full-width {
+ width: 100%;
- /* Version strings can have different character counts and this keeps
+ /* Version strings can have different character counts and this keeps
them lined up in lists */
- & b {
- min-width: 6em;
- display: inline-block;
- text-align: left;
- }
+ & b {
+ min-width: 6em;
+ display: inline-block;
+ text-align: left;
}
+ }
- &:not(.full-width) {
- width: 8em;
- margin-right: 10px;
- }
+ &:not(.full-width) {
+ width: 8em;
+ margin-right: 10px;
+ }
- // Needed for presenting component on Data Management page
- // Override full-width's width if both are set
- &.auto-width {
- width: auto;
- margin: 0.75rem;
- }
+ // Needed for presenting component on Data Management page
+ // Override full-width's width if both are set
+ &.auto-width {
+ width: auto;
+ margin: 0.75rem;
+ }
`;
const VersionString = ({
- fullWidth = true,
- autoWidth = true,
- iconColor = '#818F98',
- linkRef,
- linkHref,
- children: versionString,
- ...restProps
+ fullWidth = true,
+ autoWidth = true,
+ iconColor = '#818F98',
+ linkRef,
+ linkHref,
+ children: versionString,
+ ...restProps
}) => {
- const body = (
-
-
- {versionString}
-
- );
+ const body = (
+
+
+ {versionString}
+
+ );
- let possibleLink;
- if (linkHref) {
- if (linkRef) {
- possibleLink = (
-
- {body}
-
- );
- } else {
- possibleLink = {body} ;
- }
+ let possibleLink;
+ if (linkHref) {
+ if (linkRef) {
+ possibleLink = (
+
+ {body}
+
+ );
} else {
- possibleLink = body;
+ possibleLink = {body} ;
}
+ } else {
+ possibleLink = body;
+ }
- let classes = fullWidth ? 'full-width' : '';
- classes = autoWidth ? `${classes} auto-width` : classes;
+ let classes = fullWidth ? 'full-width' : '';
+ classes = autoWidth ? `${classes} auto-width` : classes;
- return (
-
- {possibleLink}
-
- );
+ return (
+
+ {possibleLink}
+
+ );
};
VersionString.propTypes = {
- fullWidth: PropTypes.bool,
- autoWidth: PropTypes.bool,
- iconColor: PropTypes.string,
- linkRef: PropTypes.shape({ current: PropTypes.any }),
- linkHref: PropTypes.string,
- children: PropTypes.string
+ fullWidth: PropTypes.bool,
+ autoWidth: PropTypes.bool,
+ iconColor: PropTypes.string,
+ linkRef: PropTypes.shape({ current: PropTypes.any }),
+ linkHref: PropTypes.string,
+ children: PropTypes.string
};
export default VersionString;
diff --git a/client/components/providers/AriaLiveRegionProvider/index.js b/client/components/providers/AriaLiveRegionProvider/index.js
index f32de8537..5a66230c0 100644
--- a/client/components/providers/AriaLiveRegionProvider/index.js
+++ b/client/components/providers/AriaLiveRegionProvider/index.js
@@ -5,39 +5,39 @@ import styled from '@emotion/styled';
const AriaLiveRegionContext = React.createContext();
const VisuallyHiddenAriaLiveRegion = styled.span`
- border: 0;
- clip: rect(0 0 0 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- width: 1px;
+ border: 0;
+ clip: rect(0 0 0 0);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
`;
export const useAriaLiveRegion = () => {
- const context = useContext(AriaLiveRegionContext);
- if (!context) {
- throw new Error(
- 'useAriaLiveRegion must be used within an AriaLiveRegionProvider'
- );
- }
- return context;
+ const context = useContext(AriaLiveRegionContext);
+ if (!context) {
+ throw new Error(
+ 'useAriaLiveRegion must be used within an AriaLiveRegionProvider'
+ );
+ }
+ return context;
};
export const AriaLiveRegionProvider = ({ children }) => {
- const [alertMessage, setAlertMessage] = useState('');
+ const [alertMessage, setAlertMessage] = useState('');
- return (
-
- {children}
-
- {alertMessage}
-
-
- );
+ return (
+
+ {children}
+
+ {alertMessage}
+
+
+ );
};
AriaLiveRegionProvider.propTypes = {
- children: PropTypes.node.isRequired
+ children: PropTypes.node.isRequired
};
diff --git a/client/hooks/useConfirmationModal.js b/client/hooks/useConfirmationModal.js
index a14bbe56e..983e28192 100644
--- a/client/hooks/useConfirmationModal.js
+++ b/client/hooks/useConfirmationModal.js
@@ -5,38 +5,38 @@ import useForceUpdate from './useForceUpdate';
const ConfirmationContext = createContext();
const ConfirmationModalProvider = ({ children }) => {
- const forceUpdate = useForceUpdate();
- const modalContent = useRef();
-
- const showConfirmationModal = newModalContent => {
- modalContent.current = newModalContent;
- forceUpdate();
- };
-
- const hideConfirmationModal = async () => {
- modalContent.current = null;
- forceUpdate();
- };
-
- return (
-
- {children}
- {modalContent.current}
-
- );
+ const forceUpdate = useForceUpdate();
+ const modalContent = useRef();
+
+ const showConfirmationModal = newModalContent => {
+ modalContent.current = newModalContent;
+ forceUpdate();
+ };
+
+ const hideConfirmationModal = async () => {
+ modalContent.current = null;
+ forceUpdate();
+ };
+
+ return (
+
+ {children}
+ {modalContent.current}
+
+ );
};
ConfirmationModalProvider.propTypes = {
- children: PropTypes.node.isRequired
+ children: PropTypes.node.isRequired
};
const useConfirmationModal = () => {
- const { showConfirmationModal, hideConfirmationModal } =
- useContext(ConfirmationContext);
+ const { showConfirmationModal, hideConfirmationModal } =
+ useContext(ConfirmationContext);
- return { showConfirmationModal, hideConfirmationModal };
+ return { showConfirmationModal, hideConfirmationModal };
};
export { ConfirmationModalProvider };
diff --git a/client/hooks/useDetectUa.js b/client/hooks/useDetectUa.js
index 3a6e787a5..b335684a8 100644
--- a/client/hooks/useDetectUa.js
+++ b/client/hooks/useDetectUa.js
@@ -2,24 +2,24 @@ import { useState, useEffect } from 'react';
import uaParser from 'ua-parser-js';
export function useDetectUa() {
- const [uaBrowser, setUaBrowser] = useState();
- const [uaMajor, setUaMajor] = useState();
- const [uaMinor, setUaMinor] = useState();
- const [uaPatch, setUaPatch] = useState();
+ const [uaBrowser, setUaBrowser] = useState();
+ const [uaMajor, setUaMajor] = useState();
+ const [uaMinor, setUaMinor] = useState();
+ const [uaPatch, setUaPatch] = useState();
- useEffect(() => {
- // Detect UA information
- const ua = uaParser();
- const uaBrowser = ua?.browser?.name || 'Unknown';
- const uaMajor = ua?.browser?.major || '0';
- const uaMinor = ua?.browser?.version?.split('.')?.[1] || '0';
- const uaPatch = ua?.browser?.version?.split('.')?.[2] || '0';
+ useEffect(() => {
+ // Detect UA information
+ const ua = uaParser();
+ const uaBrowser = ua?.browser?.name || 'Unknown';
+ const uaMajor = ua?.browser?.major || '0';
+ const uaMinor = ua?.browser?.version?.split('.')?.[1] || '0';
+ const uaPatch = ua?.browser?.version?.split('.')?.[2] || '0';
- setUaBrowser(uaBrowser);
- setUaMajor(uaMajor);
- setUaMinor(uaMinor);
- setUaPatch(uaPatch);
- });
+ setUaBrowser(uaBrowser);
+ setUaMajor(uaMajor);
+ setUaMinor(uaMinor);
+ setUaPatch(uaPatch);
+ });
- return { uaBrowser, uaMajor, uaMinor, uaPatch };
+ return { uaBrowser, uaMajor, uaMinor, uaPatch };
}
diff --git a/client/hooks/useForceUpdate.js b/client/hooks/useForceUpdate.js
index 923186af9..d74a3cff5 100644
--- a/client/hooks/useForceUpdate.js
+++ b/client/hooks/useForceUpdate.js
@@ -2,9 +2,9 @@ import React from 'react';
// https://stackoverflow.com/a/53215514
const useForceUpdate = () => {
- const [, updateState] = React.useState();
- const forceUpdate = React.useCallback(() => updateState({}), []);
- return forceUpdate;
+ const [, updateState] = React.useState();
+ const forceUpdate = React.useCallback(() => updateState({}), []);
+ return forceUpdate;
};
export default useForceUpdate;
diff --git a/client/hooks/useRouterQuery.js b/client/hooks/useRouterQuery.js
index e9fd2f3ce..5c174b900 100644
--- a/client/hooks/useRouterQuery.js
+++ b/client/hooks/useRouterQuery.js
@@ -1,5 +1,5 @@
import { useLocation } from 'react-router-dom';
export default function useRouterQuery() {
- return new URLSearchParams(useLocation().search);
+ return new URLSearchParams(useLocation().search);
}
diff --git a/client/hooks/useTestPlanRunIsFinished.js b/client/hooks/useTestPlanRunIsFinished.js
index 2a32b27d0..af1909796 100644
--- a/client/hooks/useTestPlanRunIsFinished.js
+++ b/client/hooks/useTestPlanRunIsFinished.js
@@ -3,36 +3,36 @@ import { useMemo } from 'react';
const { gql, useQuery } = require('@apollo/client');
const TEST_PLAN_RUN_TEST_RESULTS_COMPLETION_STATUS = gql`
- query TestPlanRunTestResultsCompletionStatus($testPlanRunId: ID!) {
- testPlanRun(id: $testPlanRunId) {
- id
- testResults {
- id
- completedAt
- }
- }
+ query TestPlanRunTestResultsCompletionStatus($testPlanRunId: ID!) {
+ testPlanRun(id: $testPlanRunId) {
+ id
+ testResults {
+ id
+ completedAt
+ }
}
+ }
`;
export const useTestPlanRunIsFinished = testPlanRunId => {
- const { data: testPlanRunCompletionQuery } = useQuery(
- TEST_PLAN_RUN_TEST_RESULTS_COMPLETION_STATUS,
- {
- variables: {
- testPlanRunId
- },
- fetchPolicy: 'cache-and-network'
- }
- );
+ const { data: testPlanRunCompletionQuery } = useQuery(
+ TEST_PLAN_RUN_TEST_RESULTS_COMPLETION_STATUS,
+ {
+ variables: {
+ testPlanRunId
+ },
+ fetchPolicy: 'cache-and-network'
+ }
+ );
- const runIsFinished = useMemo(() => {
- if (!testPlanRunCompletionQuery?.testPlanRun?.testResults.length) {
- return false;
- }
+ const runIsFinished = useMemo(() => {
+ if (!testPlanRunCompletionQuery?.testPlanRun?.testResults.length) {
+ return false;
+ }
- return testPlanRunCompletionQuery.testPlanRun.testResults.every(
- testResult => testResult.completedAt !== null
- );
- }, [testPlanRunId, testPlanRunCompletionQuery]);
+ return testPlanRunCompletionQuery.testPlanRun.testResults.every(
+ testResult => testResult.completedAt !== null
+ );
+ }, [testPlanRunId, testPlanRunCompletionQuery]);
- return { runIsFinished };
+ return { runIsFinished };
};
diff --git a/client/hooks/useTestPlanRunValidatedAssertionCounts.js b/client/hooks/useTestPlanRunValidatedAssertionCounts.js
index 6e229213f..64bb738a4 100644
--- a/client/hooks/useTestPlanRunValidatedAssertionCounts.js
+++ b/client/hooks/useTestPlanRunValidatedAssertionCounts.js
@@ -3,85 +3,84 @@ import { useQuery } from '@apollo/client';
import { TEST_PLAN_RUN_ASSERTION_RESULTS_QUERY } from '../components/TestQueueCompletionStatusListItem/queries';
export const useTestPlanRunValidatedAssertionCounts = (
- testPlanRun,
- pollInterval = null
+ testPlanRun,
+ pollInterval = null
) => {
- const {
- data: testPlanRunAssertionsQueryResult,
- startPolling,
- stopPolling,
- refetch
- } = useQuery(TEST_PLAN_RUN_ASSERTION_RESULTS_QUERY, {
- variables: {
- testPlanRunId: testPlanRun.id
- },
- fetchPolicy: 'cache-and-network',
- pollInterval
- });
-
- const testResultsLength = useMemo(() => {
- return (
- testPlanRunAssertionsQueryResult?.testPlanRun?.testResults
- ?.length || 0
- );
- }, [testPlanRunAssertionsQueryResult]);
+ const {
+ data: testPlanRunAssertionsQueryResult,
+ startPolling,
+ stopPolling,
+ refetch
+ } = useQuery(TEST_PLAN_RUN_ASSERTION_RESULTS_QUERY, {
+ variables: {
+ testPlanRunId: testPlanRun.id
+ },
+ fetchPolicy: 'cache-and-network',
+ pollInterval
+ });
- const totalPossibleAssertions = useMemo(() => {
- if (testPlanRunAssertionsQueryResult) {
- let count = 0;
- if (!testPlanRunAssertionsQueryResult?.testPlanRun) {
- return 0;
- }
- const {
- testPlanRun: { testResults }
- } = testPlanRunAssertionsQueryResult;
- for (let i = 0; i < testResults.length; i++) {
- const scenarios = testResults[i].scenarioResults;
- for (let j = 0; j < scenarios.length; j++) {
- const assertions = scenarios[j].assertionResults;
- count += assertions.length;
- }
- }
- return count;
- } else {
- return 0;
- }
- }, [testPlanRunAssertionsQueryResult]);
+ const testResultsLength = useMemo(() => {
+ return (
+ testPlanRunAssertionsQueryResult?.testPlanRun?.testResults?.length || 0
+ );
+ }, [testPlanRunAssertionsQueryResult]);
- const totalValidatedAssertions = useMemo(() => {
- if (!testPlanRunAssertionsQueryResult?.testPlanRun) {
- return 0;
+ const totalPossibleAssertions = useMemo(() => {
+ if (testPlanRunAssertionsQueryResult) {
+ let count = 0;
+ if (!testPlanRunAssertionsQueryResult?.testPlanRun) {
+ return 0;
+ }
+ const {
+ testPlanRun: { testResults }
+ } = testPlanRunAssertionsQueryResult;
+ for (let i = 0; i < testResults.length; i++) {
+ const scenarios = testResults[i].scenarioResults;
+ for (let j = 0; j < scenarios.length; j++) {
+ const assertions = scenarios[j].assertionResults;
+ count += assertions.length;
}
-
- const {
- testPlanRun: { testResults }
- } = testPlanRunAssertionsQueryResult;
- return testResults.reduce((acc, test) => {
- return (
- acc +
- (test.completedAt
- ? test.scenarioResults.reduce((acc, scenario) => {
- return acc + scenario.assertionResults.length;
- }, 0)
- : 0)
- );
- }, 0);
- }, [testPlanRunAssertionsQueryResult]);
- if (pollInterval) {
- return {
- testResultsLength,
- totalPossibleAssertions,
- totalValidatedAssertions,
- refetch,
- stopPolling,
- startPolling
- };
+ }
+ return count;
} else {
- return {
- testResultsLength,
- totalPossibleAssertions,
- totalValidatedAssertions,
- refetch
- };
+ return 0;
}
+ }, [testPlanRunAssertionsQueryResult]);
+
+ const totalValidatedAssertions = useMemo(() => {
+ if (!testPlanRunAssertionsQueryResult?.testPlanRun) {
+ return 0;
+ }
+
+ const {
+ testPlanRun: { testResults }
+ } = testPlanRunAssertionsQueryResult;
+ return testResults.reduce((acc, test) => {
+ return (
+ acc +
+ (test.completedAt
+ ? test.scenarioResults.reduce((acc, scenario) => {
+ return acc + scenario.assertionResults.length;
+ }, 0)
+ : 0)
+ );
+ }, 0);
+ }, [testPlanRunAssertionsQueryResult]);
+ if (pollInterval) {
+ return {
+ testResultsLength,
+ totalPossibleAssertions,
+ totalValidatedAssertions,
+ refetch,
+ stopPolling,
+ startPolling
+ };
+ } else {
+ return {
+ testResultsLength,
+ totalPossibleAssertions,
+ totalValidatedAssertions,
+ refetch
+ };
+ }
};
diff --git a/client/hooks/useThemedModal.js b/client/hooks/useThemedModal.js
index f058f474b..a4bca0901 100644
--- a/client/hooks/useThemedModal.js
+++ b/client/hooks/useThemedModal.js
@@ -2,84 +2,84 @@ import React, { useState, useRef, useEffect } from 'react';
import BasicThemedModal from '@components/common/BasicThemedModal';
const THEMES = {
- WARNING: 'warning',
- DANGER: 'danger'
+ WARNING: 'warning',
+ DANGER: 'danger'
};
function useThemedModal({ show, type, title, content }) {
- const focusElementRef = useRef();
+ const focusElementRef = useRef();
- const [showThemedModal, setShowThemedModal] = useState(false);
- const [themedModalType, setThemedModalType] = useState(THEMES.WARNING);
- const [themedModalTitle, setThemedModalTitle] = useState('');
- const [themedModalContent, setThemedModalContent] = useState(<>>);
- const [themedModalActions, setThemedModalActions] = useState(null);
- const [themedModalShowCloseAction, setThemedModalShowCloseAction] =
- useState(false);
+ const [showThemedModal, setShowThemedModal] = useState(false);
+ const [themedModalType, setThemedModalType] = useState(THEMES.WARNING);
+ const [themedModalTitle, setThemedModalTitle] = useState('');
+ const [themedModalContent, setThemedModalContent] = useState(<>>);
+ const [themedModalActions, setThemedModalActions] = useState(null);
+ const [themedModalShowCloseAction, setThemedModalShowCloseAction] =
+ useState(false);
- useEffect(() => {
- setShowThemedModal(showThemedModal || show);
- setThemedModalType(themedModalType || type);
- setThemedModalTitle(themedModalTitle || title);
- setThemedModalContent(themedModalContent || content);
- });
+ useEffect(() => {
+ setShowThemedModal(showThemedModal || show);
+ setThemedModalType(themedModalType || type);
+ setThemedModalTitle(themedModalTitle || title);
+ setThemedModalContent(themedModalContent || content);
+ });
- const hideThemedModal = () => {
- setShowThemedModal(false);
- setThemedModalType(THEMES.WARNING);
- setThemedModalTitle('');
- setThemedModalContent(<>>);
- setThemedModalActions(null);
- setThemedModalShowCloseAction(false);
- };
+ const hideThemedModal = () => {
+ setShowThemedModal(false);
+ setThemedModalType(THEMES.WARNING);
+ setThemedModalTitle('');
+ setThemedModalContent(<>>);
+ setThemedModalActions(null);
+ setThemedModalShowCloseAction(false);
+ };
- const onThemedModalClose = () => {
- setShowThemedModal(false);
- if (focusElementRef.current) focusElementRef.current.focus();
- };
+ const onThemedModalClose = () => {
+ setShowThemedModal(false);
+ if (focusElementRef.current) focusElementRef.current.focus();
+ };
- const setFocusRef = focusElement =>
- (focusElementRef.current = focusElement?.current || focusElement);
+ const setFocusRef = focusElement =>
+ (focusElementRef.current = focusElement?.current || focusElement);
- const focus = () => {
- if (focusElementRef.current) focusElementRef.current.focus();
- };
+ const focus = () => {
+ if (focusElementRef.current) focusElementRef.current.focus();
+ };
- const themedModal = (
-
- );
+ const themedModal = (
+
+ );
- return {
- themedModal,
- showThemedModal,
- setShowThemedModal,
- setThemedModalType,
- setThemedModalTitle,
- setThemedModalContent,
- setThemedModalActions,
- setThemedModalShowCloseAction,
- focus,
- setFocusRef,
- hideThemedModal
- };
+ return {
+ themedModal,
+ showThemedModal,
+ setShowThemedModal,
+ setThemedModalType,
+ setThemedModalTitle,
+ setThemedModalContent,
+ setThemedModalActions,
+ setThemedModalShowCloseAction,
+ focus,
+ setFocusRef,
+ hideThemedModal
+ };
}
export { useThemedModal, THEMES };
diff --git a/client/index.js b/client/index.js
index 537cd22a8..5d19d83a4 100644
--- a/client/index.js
+++ b/client/index.js
@@ -13,61 +13,61 @@ const container = document.getElementById('root');
const root = createRoot(container);
root.render(
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
const signMeInCommon = async user => {
- if (!user.username) throw new Error('Please provide a username');
+ if (!user.username) throw new Error('Please provide a username');
- const response = await fetch('/api/auth/fake-user', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(user)
- });
+ const response = await fetch('/api/auth/fake-user', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(user)
+ });
- const responseText = await response.text();
- if (!response.ok) throw responseText;
+ const responseText = await response.text();
+ if (!response.ok) throw responseText;
- location.reload();
+ location.reload();
};
window.signMeInAsAdmin = username => {
- return signMeInCommon({
- username,
- roles: [{ name: 'ADMIN' }, { name: 'TESTER' }, { name: 'VENDOR' }]
- });
+ return signMeInCommon({
+ username,
+ roles: [{ name: 'ADMIN' }, { name: 'TESTER' }, { name: 'VENDOR' }]
+ });
};
window.signMeInAsTester = username => {
- return signMeInCommon({ username, roles: [{ name: 'TESTER' }] });
+ return signMeInCommon({ username, roles: [{ name: 'TESTER' }] });
};
window.signMeInAsVendor = username => {
- return signMeInCommon({ username, roles: [{ name: 'VENDOR' }] });
+ return signMeInCommon({ username, roles: [{ name: 'VENDOR' }] });
};
window.startTestTransaction = async () => {
- const response = await fetch('/api/transactions', { method: 'POST' });
- const { transactionId } = await response.json();
- sessionStorage.setItem('currentTransactionId', transactionId);
+ const response = await fetch('/api/transactions', { method: 'POST' });
+ const { transactionId } = await response.json();
+ sessionStorage.setItem('currentTransactionId', transactionId);
};
window.endTestTransaction = async () => {
- const currentTransactionId = sessionStorage.getItem('currentTransactionId');
- if (!currentTransactionId) throw new Error('Nothing to roll back');
- await fetch('/api/transactions', {
- method: 'DELETE',
- headers: { 'x-transaction-id': currentTransactionId }
- });
- sessionStorage.removeItem('currentTransactionId');
- await resetCache();
- location.reload();
+ const currentTransactionId = sessionStorage.getItem('currentTransactionId');
+ if (!currentTransactionId) throw new Error('Nothing to roll back');
+ await fetch('/api/transactions', {
+ method: 'DELETE',
+ headers: { 'x-transaction-id': currentTransactionId }
+ });
+ sessionStorage.removeItem('currentTransactionId');
+ await resetCache();
+ location.reload();
};
diff --git a/client/jest.config.js b/client/jest.config.js
index 6a84408f2..f9f6b9cf3 100644
--- a/client/jest.config.js
+++ b/client/jest.config.js
@@ -1,20 +1,20 @@
module.exports = {
- moduleNameMapper: {
- '^@client(.*)': '/$1',
- '^@components(.*)': '/components/$1',
- '\\.(css|less)$': '/tests/__mocks__/styleMock.js',
- '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
- '/tests/__mocks__/fileMock.js'
- },
- setupFiles: ['core-js'],
- setupFilesAfterEnv: ['/tests/util/jestSuiteSetup.js'],
- globalSetup: '/tests/util/jestGlobalSetup.js',
- globalTeardown: '/tests/util/jestGlobalTeardown.js',
- testEnvironment: '/tests/util/jestPuppeteer.js',
- testTimeout: 60000,
- transform: {
- '^.+\\.(js|jsx|mjs)$': 'babel-jest'
- },
- testPathIgnorePatterns: ['/node_modules/', '/resources/'],
- coveragePathIgnorePatterns: ['/resources/']
+ moduleNameMapper: {
+ '^@client(.*)': '/$1',
+ '^@components(.*)': '/components/$1',
+ '\\.(css|less)$': '/tests/__mocks__/styleMock.js',
+ '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
+ '/tests/__mocks__/fileMock.js'
+ },
+ setupFiles: ['core-js'],
+ setupFilesAfterEnv: ['/tests/util/jestSuiteSetup.js'],
+ globalSetup: '/tests/util/jestGlobalSetup.js',
+ globalTeardown: '/tests/util/jestGlobalTeardown.js',
+ testEnvironment: '/tests/util/jestPuppeteer.js',
+ testTimeout: 60000,
+ transform: {
+ '^.+\\.(js|jsx|mjs)$': 'babel-jest'
+ },
+ testPathIgnorePatterns: ['/node_modules/', '/resources/'],
+ coveragePathIgnorePatterns: ['/resources/']
};
diff --git a/client/package.json b/client/package.json
index 233d87e81..1e167c527 100644
--- a/client/package.json
+++ b/client/package.json
@@ -23,43 +23,38 @@
},
"homepage": "https://github.com/bocoup/aria-at-app#readme",
"dependencies": {
- "@apollo/client": "^3.7.9",
- "@emotion/core": "^11.0.0",
- "@emotion/react": "^11.10.6",
- "@emotion/styled": "^11.10.6",
- "@fortawesome/fontawesome-svg-core": "^6.2.1",
- "@fortawesome/free-solid-svg-icons": "^6.2.1",
- "@fortawesome/react-fontawesome": "^0.2.0",
- "bootstrap": "^5.2.3",
- "core-js": "^3.8.0",
- "graphql": "^16.6.0",
+ "@apollo/client": "^3.10.8",
+ "@emotion/react": "^11.11.4",
+ "@emotion/styled": "^11.11.5",
+ "@fortawesome/fontawesome-svg-core": "^6.5.2",
+ "@fortawesome/free-solid-svg-icons": "^6.5.2",
+ "@fortawesome/react-fontawesome": "^0.2.2",
+ "bootstrap": "^5.3.3",
+ "core-js": "^3.37.1",
+ "graphql": "^16.9.0",
"lodash": "^4.17.21",
- "moment": "^2.25.3",
- "node-fetch": "^2.6.1",
- "object-hash": "^3.0.0",
- "octicons-react": "^1.0.4",
- "prop-types": "^15.7.2",
- "react": "^18.2.0",
- "react-bootstrap": "^2.7.0",
- "react-dom": "^18.2.0",
- "react-helmet": "^6.0.0",
+ "moment": "^2.30.1",
+ "node-fetch": "^2.7.0",
+ "octicons-react": "^1.0.9",
+ "prop-types": "^15.8.1",
+ "react": "^18.3.1",
+ "react-bootstrap": "^2.10.4",
+ "react-dom": "^18.3.1",
+ "react-helmet": "^6.1.0",
"react-html-parser": "^2.0.2",
- "react-id-generator": "^3.0.0",
- "react-responsive": "^9.0.0-beta.10",
- "react-router-bootstrap": "^0.26.2",
- "react-router-dom": "^6.8.1",
+ "react-id-generator": "^3.0.2",
+ "react-responsive": "^10.0.0",
+ "react-router-bootstrap": "^0.26.3",
+ "react-router-dom": "^6.24.1",
"shared": "1.0.0",
- "turndown": "^7.1.1",
- "ua-parser-js": "1.0.33"
+ "turndown": "^7.2.0",
+ "ua-parser-js": "1.0.38"
},
"devDependencies": {
- "@babel/code-frame": "^7.8.3",
- "@babel/core": "^7.21.4",
- "@babel/plugin-proposal-class-properties": "^7.10.1",
- "@babel/plugin-transform-runtime": "^7.21.0",
- "@babel/preset-env": "^7.8.7",
- "@babel/preset-react": "^7.8.3",
- "@lhci/cli": "^0.11.0",
+ "@babel/core": "^7.24.7",
+ "@babel/preset-env": "^7.24.7",
+ "@babel/preset-react": "^7.24.7",
+ "@lhci/cli": "^0.11.1",
"@storybook/addon-a11y": "^6.5.16",
"@storybook/addon-actions": "^6.5.16",
"@storybook/addon-controls": "^6.5.16",
@@ -68,41 +63,38 @@
"@storybook/builder-webpack5": "^6.5.16",
"@storybook/manager-webpack5": "^6.5.16",
"@storybook/react": "^6.5.16",
- "@testing-library/dom": "^9.0.0",
- "@testing-library/jest-dom": "^5.16.5",
- "@testing-library/react": "^14.0.0",
- "@testing-library/user-event": "^14.4.3",
- "babel-jest": "^29.4.3",
- "babel-loader": "^9.1.2",
+ "@testing-library/jest-dom": "^6.4.6",
+ "@testing-library/react": "^16.0.0",
+ "babel-jest": "^29.7.0",
+ "babel-loader": "^9.1.3",
"babel-plugin-lodash": "^3.3.4",
"babel-polyfill": "^6.26.0",
"copy-webpack-plugin": "^11.0.0",
- "css-loader": "^6.7.3",
- "dotenv-webpack": "^8.0.1",
+ "css-loader": "^6.11.0",
+ "dotenv-webpack": "^8.1.0",
"enzyme": "^3.11.0",
- "enzyme-adapter-react-16": "^1.15.2",
- "eslint": "^8.31.0",
- "eslint-config-prettier": "^8.6.0",
- "eslint-plugin-jest": "^27.2.1",
+ "enzyme-adapter-react-16": "^1.15.8",
+ "eslint": "^8.57.0",
+ "eslint-config-prettier": "^8.10.0",
+ "eslint-plugin-jest": "^27.9.0",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-prettier": "^4.2.1",
- "eslint-plugin-react": "^7.31.11",
+ "eslint-plugin-react": "^7.34.3",
"file-loader": "^6.0.0",
- "jest": "^29.4.1",
- "jest-environment-jsdom": "^29.3.1",
+ "jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0",
"jest-enzyme": "^7.1.2",
- "moxios": "^0.4.0",
- "prettier": "^2.8.4",
- "puppeteer": "^21.7.0",
- "storybook-addon-apollo-client": "^4.0.12",
+ "prettier": "^2.8.8",
+ "puppeteer": "^21.11.0",
+ "storybook-addon-apollo-client": "^4.1.4",
"storybook-react-router": "^1.0.8",
- "style-loader": "^3.3.1",
+ "style-loader": "^3.3.4",
"tree-kill": "^1.2.2",
- "webpack": "^5.75.0",
- "webpack-cli": "^5.0.1",
- "webpack-dev-server": "^4.11.1"
+ "webpack": "^5.92.1",
+ "webpack-cli": "^5.1.4",
+ "webpack-dev-server": "^4.15.2"
},
"resolutions": {
- "jest-environment-jsdom": "^29.3.1"
+ "jest-environment-jsdom": "^29.7.0"
}
}
diff --git a/client/routes/index.js b/client/routes/index.js
index cab1ab6f7..ffb92a6cc 100644
--- a/client/routes/index.js
+++ b/client/routes/index.js
@@ -8,7 +8,6 @@ import { Reports, Report } from '@components/Reports';
import CandidateReview from '@components/CandidateReview';
import SignupInstructions from '@components/SignupInstructions';
import TestQueue from '@components/TestQueue';
-import TestQueue2 from '@components/TestQueue2';
import TestRun from '@components/TestRun';
import UserSettings from '@components/UserSettings';
import CandidateTestPlanRun from '@components/CandidateReview/CandidateTestPlanRun';
@@ -17,72 +16,66 @@ import TestPlanVersionsPage from '../components/TestPlanVersionsPage';
import TestReview from '../components/TestReview';
export default () => (
-
- } />
- }
- />
-
-
-
- }
- />
-
-
-
- }
- />
- {/* TODO: Deprecate and remove */}
- } />
- } />
- }
- />
-
-
-
- }
- />
- }
- />
- } />
- } />
-
-
-
- }
- />
- } />
- }
- />
- } />
- } />
- } />
-
+
+ } />
+ } />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+ } />
+ }
+ />
+
+
+
+ }
+ />
+ }
+ />
+ } />
+ } />
+
+
+
+ }
+ />
+ } />
+ }
+ />
+ } />
+ } />
+ } />
+
);
diff --git a/client/static/index.css b/client/static/index.css
index ae5656889..1794ae56b 100644
--- a/client/static/index.css
+++ b/client/static/index.css
@@ -1,6 +1,6 @@
html,
body {
- width: 100%;
- height: 100vh;
- background: #eaeaea;
+ width: 100%;
+ height: 100vh;
+ background: #eaeaea;
}
diff --git a/client/stories/ProvideFeedbackModal.stories.jsx b/client/stories/ProvideFeedbackModal.stories.jsx
index a2ed6ea9f..8abfee2b9 100644
--- a/client/stories/ProvideFeedbackModal.stories.jsx
+++ b/client/stories/ProvideFeedbackModal.stories.jsx
@@ -2,37 +2,37 @@ import React from 'react';
import ProvideFeedbackModal from '../components/CandidateReview/CandidateModals/ProvideFeedbackModal';
export default {
- component: ProvideFeedbackModal,
- title: 'ProvideFeedbackModal'
+ component: ProvideFeedbackModal,
+ title: 'ProvideFeedbackModal'
};
const Template = args => ;
export const Default = Template.bind({});
Default.args = {
- at: 'JAWS',
- changesRequestedIssues: [
- { author: 'evmiguel', feedbackType: 'feedback', link: 'link to issue' },
- { author: 'evmiguel', feedbackType: 'feedback', link: 'link to issue' }
- ],
- feedbackIssues: [
- {
- author: 'evmiguel',
- feedbackType: 'changes-requested',
- link: 'link to issue'
- },
- {
- author: 'evmiguel',
- feedbackType: 'changes-requested',
- link: 'link to issue'
- },
- {
- author: 'evmiguel',
- feedbackType: 'changes-requested',
- link: 'link to issue'
- }
- ],
- show: true,
- testPlan: 'Disclosure Navigation Example',
- username: 'evmiguel'
+ at: 'JAWS',
+ changesRequestedIssues: [
+ { author: 'evmiguel', feedbackType: 'feedback', link: 'link to issue' },
+ { author: 'evmiguel', feedbackType: 'feedback', link: 'link to issue' }
+ ],
+ feedbackIssues: [
+ {
+ author: 'evmiguel',
+ feedbackType: 'changes-requested',
+ link: 'link to issue'
+ },
+ {
+ author: 'evmiguel',
+ feedbackType: 'changes-requested',
+ link: 'link to issue'
+ },
+ {
+ author: 'evmiguel',
+ feedbackType: 'changes-requested',
+ link: 'link to issue'
+ }
+ ],
+ show: true,
+ testPlan: 'Disclosure Navigation Example',
+ username: 'evmiguel'
};
diff --git a/client/stories/ThankYouModal.stories.jsx b/client/stories/ThankYouModal.stories.jsx
index 1c6828b62..c6ad4cd77 100644
--- a/client/stories/ThankYouModal.stories.jsx
+++ b/client/stories/ThankYouModal.stories.jsx
@@ -2,13 +2,13 @@ import React from 'react';
import ThankYouModal from '../components/CandidateReview/CandidateModals/ThankYouModal/index.jsx';
export default {
- component: ThankYouModal,
- title: 'ThankYouModal'
+ component: ThankYouModal,
+ title: 'ThankYouModal'
};
const Template = args => ;
export const Default = Template.bind({});
Default.args = {
- show: true
+ show: true
};
diff --git a/client/tests/AddTestToQueueWithConfirmation.test.jsx b/client/tests/AddTestToQueueWithConfirmation.test.jsx
index 9a6fa86be..71acd9c07 100644
--- a/client/tests/AddTestToQueueWithConfirmation.test.jsx
+++ b/client/tests/AddTestToQueueWithConfirmation.test.jsx
@@ -17,112 +17,110 @@ jest.mock('@apollo/client');
let mutationMock;
let mockTestPlanVersion,
- mockBrowser,
- mockAt,
- mockButtonText,
- getByTestId,
- findByRole;
+ mockBrowser,
+ mockAt,
+ mockButtonText,
+ getByTestId,
+ findByRole;
const mockTestPlanReportsQueryResult = {
- testPlanVersion: {
- testPlanReports: [
- {
- id: 'report1',
- testPlanRun: {
- id: 'testPlanRunId',
- isInitiatedByAutomation: false,
- markedFinalAt: null
- },
- isFinal: false,
- at: {
- id: '1',
- key: 'jaws'
- },
- browser: {
- id: '2',
- key: 'firefox'
- }
- }
- ]
- }
+ testPlanVersion: {
+ testPlanReports: [
+ {
+ id: 'report1',
+ testPlanRun: {
+ id: 'testPlanRunId',
+ isInitiatedByAutomation: false,
+ markedFinalAt: null
+ },
+ isFinal: false,
+ at: {
+ id: '1',
+ key: 'jaws'
+ },
+ browser: {
+ id: '2',
+ key: 'firefox'
+ }
+ }
+ ]
+ }
};
const setup = (props, mockMutation) => {
- useMutation.mockReturnValue([mockMutation, {}]);
- useQuery.mockReturnValue({
- data: mockTestPlanReportsQueryResult
- });
- return render(
-
-
-
-
-
- );
+ useMutation.mockReturnValue([mockMutation, {}]);
+ useQuery.mockReturnValue({
+ data: mockTestPlanReportsQueryResult
+ });
+ return render(
+
+
+
+
+
+ );
};
const commonSetup = mockMutation => {
- mockTestPlanVersion = { id: 5 };
- mockBrowser = { id: '2', key: 'firefox', name: 'Firefox' };
- mockAt = { id: '3', key: 'voiceover_macos', name: 'VoiceOver' };
- mockButtonText = 'Add to Test Queue';
-
- const renderResult = setup(
- {
- testPlanVersion: mockTestPlanVersion,
- browser: mockBrowser,
- at: mockAt,
- buttonText: mockButtonText
- },
- mockMutation
- );
-
- getByTestId = renderResult.getByTestId;
- findByRole = renderResult.findByRole;
+ mockTestPlanVersion = { id: 5 };
+ mockBrowser = { id: '2', key: 'firefox', name: 'Firefox' };
+ mockAt = { id: '3', key: 'voiceover_macos', name: 'VoiceOver' };
+ mockButtonText = 'Add to Test Queue';
+
+ const renderResult = setup(
+ {
+ testPlanVersion: mockTestPlanVersion,
+ browser: mockBrowser,
+ at: mockAt,
+ buttonText: mockButtonText
+ },
+ mockMutation
+ );
+
+ getByTestId = renderResult.getByTestId;
+ findByRole = renderResult.findByRole;
};
describe('AddTestToQueueWithConfirmation', () => {
- beforeEach(() => {
- mutationMock = jest.fn().mockResolvedValue(TEST_QUEUE_MUTATION_MOCK);
- commonSetup(mutationMock);
- });
-
- test('renders Button without error', async () => {
- await waitFor(() =>
- expect(getByTestId('add-button')).toBeInTheDocument()
- );
- });
-
- test('Button has correct text', async () => {
- await waitFor(() =>
- expect(getByTestId('add-button')).toHaveTextContent(mockButtonText)
- );
- });
+ beforeEach(() => {
+ mutationMock = jest.fn().mockResolvedValue(TEST_QUEUE_MUTATION_MOCK);
+ commonSetup(mutationMock);
+ });
+
+ test('renders Button without error', async () => {
+ await waitFor(() => expect(getByTestId('add-button')).toBeInTheDocument());
+ });
+
+ test('Button has correct text', async () => {
+ await waitFor(() =>
+ expect(getByTestId('add-button')).toHaveTextContent(mockButtonText)
+ );
+ });
- test('renders BasicModal without error', async () => {
- fireEvent.click(getByTestId('add-button'));
+ test('renders BasicModal without error', async () => {
+ fireEvent.click(getByTestId('add-button'));
- await waitFor(async () => {
- const modal = await findByRole('dialog');
- expect(modal).toBeInTheDocument();
- });
+ await waitFor(async () => {
+ const modal = await findByRole('dialog');
+ expect(modal).toBeInTheDocument();
});
-
- test('calls mutation on button click with correct variables', async () => {
- fireEvent.click(getByTestId('add-button'));
-
- await waitFor(() => {
- expect(mutationMock).toHaveBeenCalled();
- expect(mutationMock).toHaveBeenCalledWith({
- variables: {
- testPlanVersionId: mockTestPlanVersion.id,
- atId: mockAt.id,
- browserId: mockBrowser.id
- }
- });
- });
+ });
+
+ test('calls mutation on button click with correct variables', async () => {
+ fireEvent.click(getByTestId('add-button'));
+
+ await waitFor(() => {
+ expect(mutationMock).toHaveBeenCalled();
+ expect(mutationMock).toHaveBeenCalledWith({
+ variables: {
+ testPlanVersionId: mockTestPlanVersion.id,
+ atId: mockAt.id,
+ browserId: mockBrowser.id
+ }
+ });
});
+ });
});
diff --git a/client/tests/App.test.jsx b/client/tests/App.test.jsx
index 45b5c66e1..8d8400f69 100644
--- a/client/tests/App.test.jsx
+++ b/client/tests/App.test.jsx
@@ -11,15 +11,15 @@ import GraphQLProvider from '../components/GraphQLProvider';
Enzyme.configure({ adapter: new EnzymeAdapter() });
const setup = () => {
- const wrapper = shallow(
-
-
-
- ).dive();
- return wrapper;
+ const wrapper = shallow(
+
+
+
+ ).dive();
+ return wrapper;
};
test('renders without crashing', () => {
- const wrapper = setup();
- expect(wrapper).toBeTruthy();
+ const wrapper = setup();
+ expect(wrapper).toBeTruthy();
});
diff --git a/client/tests/AssignTesterDropdown.test.jsx b/client/tests/AssignTesterDropdown.test.jsx
index 1768b0867..4fffe9257 100644
--- a/client/tests/AssignTesterDropdown.test.jsx
+++ b/client/tests/AssignTesterDropdown.test.jsx
@@ -1,368 +1,355 @@
/**
* @jest-environment jsdom
*/
-import React from 'react';
+import React, { act } from 'react';
import {
- render,
- fireEvent,
- waitFor,
- screen,
- cleanup
+ render,
+ fireEvent,
+ waitFor,
+ screen,
+ cleanup
} from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
-import AssignTesterDropdown from '../components/TestQueue/AssignTesterDropdown';
+import AssignTesterDropdown from '../components/common/AssignTesterDropdown';
import {
- ASSIGN_TESTER_MUTATION,
- REMOVE_TESTER_MUTATION,
- TEST_PLAN_REPORT_AT_BROWSER_QUERY
-} from '../components/TestQueue/queries';
-import { SCHEDULE_COLLECTION_JOB_MUTATION } from '../components/AddTestToQueueWithConfirmation/queries';
-import '@testing-library/jest-dom/extend-expect';
+ ASSIGN_TESTER_MUTATION,
+ REMOVE_TESTER_MUTATION,
+ TEST_PLAN_REPORT_AT_BROWSER_QUERY
+} from '@components/common/AssignTesterDropdown/queries';
+import { SCHEDULE_COLLECTION_JOB_MUTATION } from '@components/AddTestToQueueWithConfirmation/queries';
+import '@testing-library/jest-dom';
jest.mock('@apollo/client', () => {
- const original = jest.requireActual('@apollo/client');
- return {
- ...original,
- useMutation: jest.fn()
- };
+ const original = jest.requireActual('@apollo/client');
+ return {
+ ...original,
+ useMutation: jest.fn()
+ };
});
const mockPossibleTesters = [
- { id: '1', username: 'bee', isBot: false, ats: [] },
- { id: '2', username: 'puppy', isBot: false, ats: [] },
- {
- id: '9999',
- username: 'NVDA Bot',
- isBot: true,
- ats: [{ id: '2', key: 'nvda' }]
- },
- {
- id: '9998',
- username: 'VoiceOver Bot',
- isBot: true,
- ats: [{ id: '3', key: 'voiceover_macos' }]
- }
+ { id: '1', username: 'bee', isBot: false, ats: [] },
+ { id: '2', username: 'puppy', isBot: false, ats: [] },
+ {
+ id: '9999',
+ username: 'NVDA Bot',
+ isBot: true,
+ ats: [{ id: '2', key: 'nvda' }]
+ },
+ {
+ id: '9998',
+ username: 'VoiceOver Bot',
+ isBot: true,
+ ats: [{ id: '3', key: 'voiceover_macos' }]
+ }
];
const mockProps = {
- testPlanReportId: 'report1',
- possibleTesters: mockPossibleTesters,
- onChange: jest.fn(),
- testPlanRun: null,
- draftTestPlanRuns: [],
- label: 'Assign testers',
- setAlertMessage: jest.fn()
+ testPlanReportId: 'report1',
+ possibleTesters: mockPossibleTesters,
+ onChange: jest.fn(),
+ testPlanRun: null,
+ draftTestPlanRuns: [],
+ label: 'Assign testers',
+ setAlertMessage: jest.fn()
};
import { useMutation } from '@apollo/client';
-import { act } from 'react-dom/test-utils';
// Mock useMutation hook
useMutation.mockImplementation(mutation => {
- let response;
-
- if (mutation === ASSIGN_TESTER_MUTATION) {
- response = 'ASSIGN';
- } else if (mutation === REMOVE_TESTER_MUTATION) {
- response = 'REMOVE';
- } else if (mutation === SCHEDULE_COLLECTION_JOB_MUTATION) {
- response = 'SCHEDULE';
- }
+ let response;
+
+ if (mutation === ASSIGN_TESTER_MUTATION) {
+ response = 'ASSIGN';
+ } else if (mutation === REMOVE_TESTER_MUTATION) {
+ response = 'REMOVE';
+ } else if (mutation === SCHEDULE_COLLECTION_JOB_MUTATION) {
+ response = 'SCHEDULE';
+ }
- return [jest.fn(() => response), { loading: false, error: null }];
+ return [jest.fn(() => response), { loading: false, error: null }];
});
// Mocked GraphQL responses
const getMocks = (atKey, browserKey) => {
- const at = {
- nvda: {
- id: '2',
- name: 'NVDA',
- key: 'nvda'
- },
- jaws: {
- id: '1',
- name: 'JAWS',
- key: 'jaws'
- },
- voiceover_macos: {
- id: '3',
- name: 'VoiceOver for MacOS',
- key: 'voiceover_macos'
- }
- }[atKey];
-
- const browser = {
- chrome: {
- id: '2',
- name: 'Chrome',
- key: 'chrome'
- },
- safari_macos: {
- id: '3',
- name: 'Safari for MacOS',
- key: 'safari_macos'
- },
- voiceover_macos: {
- id: '3',
- name: 'VoiceOver for MacOS',
- key: 'voiceover_macos'
+ const at = {
+ nvda: {
+ id: '2',
+ name: 'NVDA',
+ key: 'nvda'
+ },
+ jaws: {
+ id: '1',
+ name: 'JAWS',
+ key: 'jaws'
+ },
+ voiceover_macos: {
+ id: '3',
+ name: 'VoiceOver for MacOS',
+ key: 'voiceover_macos'
+ }
+ }[atKey];
+
+ const browser = {
+ chrome: {
+ id: '2',
+ name: 'Chrome',
+ key: 'chrome'
+ },
+ safari_macos: {
+ id: '3',
+ name: 'Safari for MacOS',
+ key: 'safari_macos'
+ },
+ voiceover_macos: {
+ id: '3',
+ name: 'VoiceOver for MacOS',
+ key: 'voiceover_macos'
+ }
+ }[browserKey];
+
+ if (!at) throw new Error('Unsupported AT key for mocks');
+ if (!browser) throw new Error('Unsupported browser key for mocks');
+
+ return [
+ {
+ request: {
+ query: ASSIGN_TESTER_MUTATION,
+ variables: {
+ testReportId: 'report1',
+ testerId: '1'
}
- }[browserKey];
-
- if (!at) throw new Error('Unsupported AT key for mocks');
- if (!browser) throw new Error('Unsupported browser key for mocks');
-
- return [
- {
- request: {
- query: ASSIGN_TESTER_MUTATION,
- variables: {
- testReportId: 'report1',
- testerId: '1'
- }
- },
- result: {
- data: {
- testPlanReport: {
- assignTester: {
- testPlanReport: {
- draftTestPlanRuns: [
- {
- initiatedByAutomation: false,
- tester: {
- id: '1',
- username: 'bee'
- }
- }
- ]
- }
- }
- }
- }
- }
- },
- {
- request: {
- query: REMOVE_TESTER_MUTATION,
- variables: {
- testReportId: 'report1',
- testerId: '1'
- }
- },
- result: {
- data: {
- testPlanReport: {
- deleteTestPlanRun: {
- testPlanReport: {
- draftTestPlanRuns: []
- }
- }
- }
- }
- }
- },
- {
- request: {
- query: SCHEDULE_COLLECTION_JOB_MUTATION,
- variables: {
- testPlanReportId: 'report1'
- }
- },
- result: {
- data: {
- scheduleCollectionJob: {
- id: 'some-job-id',
- status: 'pending'
+ },
+ result: {
+ data: {
+ testPlanReport: {
+ assignTester: {
+ testPlanReport: {
+ draftTestPlanRuns: [
+ {
+ initiatedByAutomation: false,
+ tester: {
+ id: '1',
+ username: 'bee'
}
- }
+ }
+ ]
+ }
}
- },
- {
- request: {
- query: TEST_PLAN_REPORT_AT_BROWSER_QUERY,
- variables: {
- testPlanReportId: 'report1'
- }
- },
- result: {
- data: {
- testPlanReport: {
- id: 'report1',
- at,
- browser
- }
- }
+ }
+ }
+ }
+ },
+ {
+ request: {
+ query: REMOVE_TESTER_MUTATION,
+ variables: {
+ testReportId: 'report1',
+ testerId: '1'
+ }
+ },
+ result: {
+ data: {
+ testPlanReport: {
+ deleteTestPlanRun: {
+ testPlanReport: {
+ draftTestPlanRuns: []
+ }
}
+ }
}
- ];
+ }
+ },
+ {
+ request: {
+ query: SCHEDULE_COLLECTION_JOB_MUTATION,
+ variables: {
+ testPlanReportId: 'report1'
+ }
+ },
+ result: {
+ data: {
+ scheduleCollectionJob: {
+ id: 'some-job-id',
+ status: 'pending'
+ }
+ }
+ }
+ },
+ {
+ request: {
+ query: TEST_PLAN_REPORT_AT_BROWSER_QUERY,
+ variables: {
+ testPlanReportId: 'report1'
+ }
+ },
+ result: {
+ data: {
+ testPlanReport: {
+ id: 'report1',
+ at,
+ browser
+ }
+ }
+ }
+ }
+ ];
};
describe('AssignTesterDropdown', () => {
- beforeEach(() => {
- cleanup();
+ beforeEach(() => {
+ cleanup();
+ });
+
+ it('renders without crashing', () => {
+ render(
+
+
+
+ );
+ expect(screen.getByLabelText('Assign testers')).toBeInTheDocument();
+ });
+
+ it('assigns tester correctly and calls assignTester mutation', async () => {
+ render(
+
+
+
+ );
+
+ const button = await screen.getByRole('button', {
+ name: /assign testers/i
+ });
+ fireEvent.click(button);
+
+ const items = await screen.findAllByText(/bee/);
+ expect(items.length).toBe(1);
+ fireEvent.click(items[0]);
+
+ await waitFor(async () => {
+ expect(useMutation).toHaveBeenCalledWith(ASSIGN_TESTER_MUTATION);
+ expect(mockProps.onChange).toHaveBeenCalledTimes(1);
+ expect(mockProps.setAlertMessage).toHaveBeenCalledTimes(1);
+ expect(mockProps.setAlertMessage).toHaveBeenCalledWith(
+ expect.stringContaining('bee now checked')
+ );
});
+ });
- it('renders without crashing', () => {
- render(
-
-
-
- );
- expect(screen.getByLabelText('Assign testers')).toBeInTheDocument();
+ it('assigns bot correctly and calls scheduleCollection mutation', async () => {
+ render(
+
+
+
+ );
+
+ const button = await screen.getByRole('button', {
+ name: /assign testers/i
});
+ fireEvent.click(button);
+
+ const items = await screen.findAllByText(/NVDA Bot/);
+ expect(items.length).toBe(1);
+ fireEvent.click(items[0]);
- it('assigns tester correctly and calls assignTester mutation', async () => {
- render(
-
-
-
- );
-
- const button = await screen.getByRole('button', {
- name: /assign testers/i
- });
- fireEvent.click(button);
-
- const items = await screen.findAllByText(/bee/);
- expect(items.length).toBe(1);
- fireEvent.click(items[0]);
-
- await waitFor(async () => {
- expect(useMutation).toHaveBeenCalledWith(ASSIGN_TESTER_MUTATION);
- expect(mockProps.onChange).toHaveBeenCalledTimes(1);
- expect(mockProps.setAlertMessage).toHaveBeenCalledTimes(1);
- expect(mockProps.setAlertMessage).toHaveBeenCalledWith(
- expect.stringContaining('bee now checked')
- );
- });
+ await waitFor(() => {
+ expect(useMutation).toHaveBeenCalledWith(
+ SCHEDULE_COLLECTION_JOB_MUTATION
+ );
+ expect(mockProps.onChange).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ it('does not list bot when run does not support automation', async () => {
+ await act(async () => {
+ render(
+
+
+
+ );
+ });
+ let button;
+ await waitFor(async () => {
+ button = await screen.getByRole('button', {
+ name: /assign testers/i
+ });
});
- it('assigns bot correctly and calls scheduleCollection mutation', async () => {
- render(
-
-
-
- );
-
- const button = await screen.getByRole('button', {
- name: /assign testers/i
- });
- fireEvent.click(button);
-
- const items = await screen.findAllByText(/NVDA Bot/);
- expect(items.length).toBe(1);
- fireEvent.click(items[0]);
-
- await waitFor(() => {
- expect(useMutation).toHaveBeenCalledWith(
- SCHEDULE_COLLECTION_JOB_MUTATION
- );
- expect(mockProps.onChange).toHaveBeenCalledTimes(1);
- });
+ await act(async () => {
+ fireEvent.click(button);
});
- it('does not list bot when run does not support automation', async () => {
- await act(async () => {
- render(
-
-
-
- );
- });
- let button;
- await waitFor(async () => {
- button = await screen.getByRole('button', {
- name: /assign testers/i
- });
- });
-
- await act(async () => {
- fireEvent.click(button);
- });
-
- await waitFor(async () => {
- const items = await screen.queryByText(/NVDA Bot/);
- expect(items).toBeNull();
- });
+ await waitFor(async () => {
+ const items = await screen.queryByText(/NVDA Bot/);
+ expect(items).toBeNull();
+ });
+ });
+
+ it('only lists supported bot', async () => {
+ await act(async () => {
+ render(
+
+
+
+ );
+ });
+ let button;
+ await waitFor(async () => {
+ button = await screen.getByRole('button', {
+ name: /assign testers/i
+ });
});
- it('only lists supported bot', async () => {
- await act(async () => {
- render(
-
-
-
- );
- });
- let button;
- await waitFor(async () => {
- button = await screen.getByRole('button', {
- name: /assign testers/i
- });
- });
-
- await act(async () => {
- fireEvent.click(button);
- });
-
- await waitFor(async () => {
- const items = await screen.queryByText(/NVDA Bot/);
- expect(items).toBeNull();
- });
-
- await waitFor(async () => {
- const items = await screen.queryAllByText(/VoiceOver Bot/);
- expect(items).toHaveLength(1);
- });
+ await act(async () => {
+ fireEvent.click(button);
});
- it('removes tester correctly and calls removeTester mutation', async () => {
- mockProps.draftTestPlanRuns = [
- {
- initiatedByAutomation: false,
- tester: {
- id: '1',
- username: 'bee'
- }
- }
- ];
-
- render(
-
-
-
- );
-
- const button = await screen.getByRole('button', {
- name: /assign testers/i
- });
-
- fireEvent.click(button);
-
- const items = await screen.findAllByText(/bee/);
- fireEvent.click(items[0]);
-
- await waitFor(() => {
- expect(useMutation).toHaveBeenCalledWith(REMOVE_TESTER_MUTATION);
- expect(mockProps.setAlertMessage).toHaveBeenCalledWith(
- expect.stringContaining('bee now unchecked')
- );
- });
+ await waitFor(async () => {
+ const items = await screen.queryByText(/NVDA Bot/);
+ expect(items).toBeNull();
+ });
+
+ await waitFor(async () => {
+ const items = await screen.queryAllByText(/VoiceOver Bot/);
+ expect(items).toHaveLength(1);
+ });
+ });
+
+ it('removes tester correctly and calls removeTester mutation', async () => {
+ mockProps.draftTestPlanRuns = [
+ {
+ initiatedByAutomation: false,
+ tester: {
+ id: '1',
+ username: 'bee'
+ }
+ }
+ ];
+
+ render(
+
+
+
+ );
+
+ const button = await screen.getByRole('button', {
+ name: /assign testers/i
+ });
+
+ fireEvent.click(button);
+
+ const items = await screen.findAllByText(/bee/);
+ fireEvent.click(items[0]);
+
+ await waitFor(() => {
+ expect(useMutation).toHaveBeenCalledWith(REMOVE_TESTER_MUTATION);
+ expect(mockProps.setAlertMessage).toHaveBeenCalledWith(
+ expect.stringContaining('bee now unchecked')
+ );
});
+ });
});
diff --git a/client/tests/AtVersions.test.js b/client/tests/AtVersions.test.js
deleted file mode 100644
index 368b74c1e..000000000
--- a/client/tests/AtVersions.test.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import getPage from './util/getPage';
-
-describe('AT Version UI', () => {
- test('should add, edit, then remove an AtVersion', async () => {
- /* prettier-ignore */
- await getPage({ role: 'admin', url: '/test-queue' }, async page => {
- const openTrayIfClosed = async () => {
- await page.waitForSelector('button ::-p-text(Manage Assistive Technology Versions)');
- const isTrayClosed = !!(await page.$('::-p-text(Select an assistive technology and manage its versions)'));
- if (isTrayClosed) {
- await page.click('button ::-p-text(Manage Assistive Technology Versions)');
- await page.waitForSelector('::-p-text(Select an assistive technology and manage its versions)');
- }
- };
- await openTrayIfClosed();
- await page.click('button ::-p-text(Add a New Version)');
- await page.waitForSelector('.modal-title ::-p-text(Add a New Version for JAWS)');
- await page.waitForSelector('.modal-body .form-group:nth-child(1) ::-p-text(Version Number)');
- await page.waitForSelector('.modal-body .form-group:nth-child(2) ::-p-text(Approximate date of availability)');
- await page.type('.modal-body .form-group:nth-child(1) input', '99.0.1');
- await page.type('.modal-body .form-group:nth-child(2) input', '01-01-2000');
- await page.click('.modal-footer button ::-p-text(Add Version)');
- await page.waitForNetworkIdle({ idleTime: 5000 });
- await page.click('.modal-footer button ::-p-text(Ok)');
- await page.waitForSelector('.at-versions-container option:nth-child(2) ::-p-text(99.0.1)');
- const optionValue = await page.$eval('.at-versions-container option:nth-child(2)', option => option.value);
- await page.select('.at-versions-container select', optionValue);
- await page.click('.at-versions-container button ::-p-text(Edit)');
- const input = await page.waitForSelector('.modal-body .form-group:nth-child(1) input');
- for (let i = 0; i < 6; i += 1) {
- await input.press('Backspace');
- }
- await page.type('.modal-body .form-group:nth-child(1) input', '99.0.99');
- await page.click('.modal-footer button ::-p-text(Save)');
- await page.waitForNetworkIdle({ idleTime: 5000 });
- await page.click('.modal-footer button ::-p-text(Ok)');
- await page.waitForSelector('.at-versions-container option ::-p-text(99.0.99)');
- await page.select('.at-versions-container select', optionValue);
- await page.click('.at-versions-container button ::-p-text(Remove)');
- await page.waitForSelector('.modal-title ::-p-text(Remove JAWS Version 99.0.99)');
- await page.click('.modal-footer button ::-p-text(Remove)');
- await page.waitForNetworkIdle();
- const option = await page.$('.at-versions-container option ::-p-text(99.0.99)');
- expect(option).toBeNull();
- });
- });
-});
diff --git a/client/tests/BasicModal.test.jsx b/client/tests/BasicModal.test.jsx
index dc83d1518..d67660136 100644
--- a/client/tests/BasicModal.test.jsx
+++ b/client/tests/BasicModal.test.jsx
@@ -3,138 +3,130 @@
*/
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
-import '@testing-library/jest-dom/extend-expect';
+import '@testing-library/jest-dom';
import BasicModal from '../components/common/BasicModal';
const MockCustomComponent = ({ customProp }) => {customProp} ; // eslint-disable-line react/prop-types
describe('BasicModal', () => {
- test('renders modal when show is true', () => {
- render(
-
- );
- expect(screen.getByRole('dialog')).toBeInTheDocument();
- });
+ test('renders modal when show is true', () => {
+ render(
+
+ );
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
+ });
- test('does not render modal when show is false', () => {
- render(
-
- );
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
- });
+ test('does not render modal when show is false', () => {
+ render(
+
+ );
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+ });
- test('renders title and content correctly', () => {
- render(
-
- );
- expect(screen.getByText('My Title')).toBeInTheDocument();
- expect(screen.getByText('My Content')).toBeInTheDocument();
- });
+ test('renders title and content correctly', () => {
+ render( );
+ expect(screen.getByText('My Title')).toBeInTheDocument();
+ expect(screen.getByText('My Content')).toBeInTheDocument();
+ });
- test('multiple action buttons trigger correct functions', () => {
- const saveFunction = jest.fn();
- const deleteFunction = jest.fn();
- render(
-
- );
- fireEvent.click(screen.getByText('Save'));
- expect(saveFunction).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByText('Delete'));
- expect(deleteFunction).toHaveBeenCalledTimes(1);
- expect(saveFunction).toHaveBeenCalledTimes(1);
- });
+ test('multiple action buttons trigger correct functions', () => {
+ const saveFunction = jest.fn();
+ const deleteFunction = jest.fn();
+ render(
+
+ );
+ fireEvent.click(screen.getByText('Save'));
+ expect(saveFunction).toHaveBeenCalledTimes(1);
+ fireEvent.click(screen.getByText('Delete'));
+ expect(deleteFunction).toHaveBeenCalledTimes(1);
+ expect(saveFunction).toHaveBeenCalledTimes(1);
+ });
- test('close button triggers handleClose function', () => {
- const handleClose = jest.fn();
- render(
-
- );
- fireEvent.click(screen.getByText('Cancel'));
- expect(handleClose).toHaveBeenCalledTimes(1);
- });
+ test('close button triggers handleClose function', () => {
+ const handleClose = jest.fn();
+ render(
+
+ );
+ fireEvent.click(screen.getByText('Cancel'));
+ expect(handleClose).toHaveBeenCalledTimes(1);
+ });
- test('cancel button triggers handleClose function when cancelButton is true', () => {
- const handleClose = jest.fn();
- render(
-
- );
- fireEvent.click(screen.getByText('Cancel'));
- expect(handleClose).toHaveBeenCalledTimes(1);
- });
- test('renders custom component in actions', () => {
- render(
-
- );
+ test('cancel button triggers handleClose function when cancelButton is true', () => {
+ const handleClose = jest.fn();
+ render(
+
+ );
+ fireEvent.click(screen.getByText('Cancel'));
+ expect(handleClose).toHaveBeenCalledTimes(1);
+ });
+ test('renders custom component in actions', () => {
+ render(
+
+ );
- expect(screen.getByText('Custom Button')).toBeInTheDocument();
- });
+ expect(screen.getByText('Custom Button')).toBeInTheDocument();
+ });
- test('custom component triggers appropriate action', () => {
- const customAction = jest.fn();
+ test('custom component triggers appropriate action', () => {
+ const customAction = jest.fn();
- render(
- (
-
- Custom Action
-
- ),
- props: {}
- }
- ]}
- />
- );
+ render(
+ (
+ Custom Action
+ ),
+ props: {}
+ }
+ ]}
+ />
+ );
- fireEvent.click(screen.getByText('Custom Action'));
- expect(customAction).toHaveBeenCalledTimes(1);
- });
+ fireEvent.click(screen.getByText('Custom Action'));
+ expect(customAction).toHaveBeenCalledTimes(1);
+ });
- test('has aria-labelledby matching the modal title id', () => {
- const { getByRole, getByText } = render(
-
- );
+ test('has aria-labelledby matching the modal title id', () => {
+ const { getByRole, getByText } = render(
+
+ );
- const modal = getByRole('dialog');
- const title = getByText('Test Title');
+ const modal = getByRole('dialog');
+ const title = getByText('Test Title');
- expect(modal).toHaveAttribute('aria-labelledby', title.id);
- });
+ expect(modal).toHaveAttribute('aria-labelledby', title.id);
+ });
});
diff --git a/client/tests/BotRunTestStatusList.test.jsx b/client/tests/BotRunTestStatusList.test.jsx
index 2aec3b4c8..d6e5cc5a3 100644
--- a/client/tests/BotRunTestStatusList.test.jsx
+++ b/client/tests/BotRunTestStatusList.test.jsx
@@ -6,168 +6,168 @@ import { render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import BotRunTestStatusList from '../components/BotRunTestStatusList';
import { TEST_PLAN_RUNS_TEST_RESULTS_QUERY } from '../components/BotRunTestStatusList/queries';
-import '@testing-library/jest-dom/extend-expect';
+import '@testing-library/jest-dom';
import { COLLECTION_JOB_STATUS } from '../../server/util/enums';
const getMocks = testPlanRuns => {
- const testPlanRunMock = {
- request: {
- query: TEST_PLAN_RUNS_TEST_RESULTS_QUERY,
- variables: { testPlanReportId: '1' }
- },
- result: { data: { testPlanRuns } }
- };
-
- return [testPlanRunMock];
+ const testPlanRunMock = {
+ request: {
+ query: TEST_PLAN_RUNS_TEST_RESULTS_QUERY,
+ variables: { testPlanReportId: '1' }
+ },
+ result: { data: { testPlanRuns } }
+ };
+
+ return [testPlanRunMock];
};
test('correctly displays statuses for single COMPLETED test run', async () => {
- const testPlanRuns = [
- {
- id: '0',
- testResults: new Array(3).fill(null),
- tester: { username: 'bot' },
- collectionJob: {
- status: COLLECTION_JOB_STATUS.COMPLETED,
- testStatus: [
- { status: COLLECTION_JOB_STATUS.COMPLETED },
- { status: COLLECTION_JOB_STATUS.COMPLETED },
- { status: COLLECTION_JOB_STATUS.COMPLETED }
- ]
- }
- }
- ];
-
- const mocks = getMocks(testPlanRuns);
-
- const screen = render(
-
-
-
- );
-
- const { getByText } = screen;
-
- await waitFor(() => {
- expect(getByText('3 Tests Completed')).toBeInTheDocument();
- expect(getByText('0 Tests Queued')).toBeInTheDocument();
- });
+ const testPlanRuns = [
+ {
+ id: '0',
+ testResults: new Array(3).fill(null),
+ tester: { username: 'bot' },
+ collectionJob: {
+ status: COLLECTION_JOB_STATUS.COMPLETED,
+ testStatus: [
+ { status: COLLECTION_JOB_STATUS.COMPLETED },
+ { status: COLLECTION_JOB_STATUS.COMPLETED },
+ { status: COLLECTION_JOB_STATUS.COMPLETED }
+ ]
+ }
+ }
+ ];
+
+ const mocks = getMocks(testPlanRuns);
+
+ const screen = render(
+
+
+
+ );
+
+ const { getByText } = screen;
+
+ await waitFor(() => {
+ expect(getByText('3 Tests Completed')).toBeInTheDocument();
+ expect(getByText('0 Tests Queued')).toBeInTheDocument();
+ });
});
test('correctly ignores test results from a human-submitted test plan run', async () => {
- const testPlanRuns = [
- {
- id: '0',
- testResults: new Array(2).fill(null),
- tester: { username: 'bot' },
- collectionJob: {
- status: COLLECTION_JOB_STATUS.COMPLETED,
- testStatus: [
- { status: COLLECTION_JOB_STATUS.COMPLETED },
- { status: COLLECTION_JOB_STATUS.COMPLETED }
- ]
- }
- },
- {
- id: '1',
- testResults: new Array(2).fill(null),
- tester: { username: 'human' },
- collectionJob: null
- }
- ];
-
- const mocks = getMocks(testPlanRuns);
-
- const { getByText } = render(
-
-
-
- );
-
- await waitFor(async () => {
- expect(getByText('2 Tests Completed')).toBeInTheDocument();
- expect(getByText('0 Tests Queued')).toBeInTheDocument();
- });
+ const testPlanRuns = [
+ {
+ id: '0',
+ testResults: new Array(2).fill(null),
+ tester: { username: 'bot' },
+ collectionJob: {
+ status: COLLECTION_JOB_STATUS.COMPLETED,
+ testStatus: [
+ { status: COLLECTION_JOB_STATUS.COMPLETED },
+ { status: COLLECTION_JOB_STATUS.COMPLETED }
+ ]
+ }
+ },
+ {
+ id: '1',
+ testResults: new Array(2).fill(null),
+ tester: { username: 'human' },
+ collectionJob: null
+ }
+ ];
+
+ const mocks = getMocks(testPlanRuns);
+
+ const { getByText } = render(
+
+
+
+ );
+
+ await waitFor(async () => {
+ expect(getByText('2 Tests Completed')).toBeInTheDocument();
+ expect(getByText('0 Tests Queued')).toBeInTheDocument();
+ });
});
test('correctly displays statuses for CANCELLED test run', async () => {
- const testPlanRuns = [
- {
- id: '0',
- testResults: new Array(2).fill(null),
- tester: { username: 'bot' },
- collectionJob: {
- status: COLLECTION_JOB_STATUS.CANCELLED,
- testStatus: [
- { status: COLLECTION_JOB_STATUS.COMPLETED },
- { status: COLLECTION_JOB_STATUS.COMPLETED },
- { status: COLLECTION_JOB_STATUS.CANCELLED }
- ]
- }
- }
- ];
-
- const mocks = getMocks(testPlanRuns);
-
- const { getByText } = render(
-
-
-
- );
-
- await waitFor(() => {
- expect(getByText('2 Tests Completed')).toBeInTheDocument();
- expect(getByText('0 Tests Queued')).toBeInTheDocument();
- expect(getByText('1 Test Cancelled')).toBeInTheDocument();
- });
+ const testPlanRuns = [
+ {
+ id: '0',
+ testResults: new Array(2).fill(null),
+ tester: { username: 'bot' },
+ collectionJob: {
+ status: COLLECTION_JOB_STATUS.CANCELLED,
+ testStatus: [
+ { status: COLLECTION_JOB_STATUS.COMPLETED },
+ { status: COLLECTION_JOB_STATUS.COMPLETED },
+ { status: COLLECTION_JOB_STATUS.CANCELLED }
+ ]
+ }
+ }
+ ];
+
+ const mocks = getMocks(testPlanRuns);
+
+ const { getByText } = render(
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(getByText('2 Tests Completed')).toBeInTheDocument();
+ expect(getByText('0 Tests Queued')).toBeInTheDocument();
+ expect(getByText('1 Test Cancelled')).toBeInTheDocument();
+ });
});
test('correctly displays statuses for multiple RUNNING and QUEUED test runs', async () => {
- const testPlanRuns = [
- {
- id: '0',
- testResults: new Array(2).fill(null),
- tester: { username: 'bot' },
- collectionJob: {
- status: COLLECTION_JOB_STATUS.RUNNING,
- testStatus: [
- { status: COLLECTION_JOB_STATUS.RUNNING },
- { status: COLLECTION_JOB_STATUS.COMPLETED },
- { status: COLLECTION_JOB_STATUS.QUEUED }
- ]
- }
- },
- {
- id: '1',
- testResults: new Array(2).fill(null),
- tester: { username: 'bot' },
- collectionJob: {
- status: COLLECTION_JOB_STATUS.CANCELLED,
- testStatus: [
- { status: COLLECTION_JOB_STATUS.CANCELLED },
- { status: COLLECTION_JOB_STATUS.COMPLETED },
- { status: COLLECTION_JOB_STATUS.CANCELLED }
- ]
- }
- }
- ];
-
- const mocks = getMocks(testPlanRuns);
-
- const { getByText } = render(
-
-
-
- );
-
- await waitFor(async () => {
- // Wait for the component to update
- // Imperfect but prevents needing to detect loading removal
- await setTimeout(() => {
- expect(getByText('1 Test Running')).toBeInTheDocument();
- expect(getByText('2 Tests Completed')).toBeInTheDocument();
- expect(getByText('1 Test Queued')).toBeInTheDocument();
- expect(getByText('2 Tests Cancelled')).toBeInTheDocument();
- }, 500);
- });
+ const testPlanRuns = [
+ {
+ id: '0',
+ testResults: new Array(2).fill(null),
+ tester: { username: 'bot' },
+ collectionJob: {
+ status: COLLECTION_JOB_STATUS.RUNNING,
+ testStatus: [
+ { status: COLLECTION_JOB_STATUS.RUNNING },
+ { status: COLLECTION_JOB_STATUS.COMPLETED },
+ { status: COLLECTION_JOB_STATUS.QUEUED }
+ ]
+ }
+ },
+ {
+ id: '1',
+ testResults: new Array(2).fill(null),
+ tester: { username: 'bot' },
+ collectionJob: {
+ status: COLLECTION_JOB_STATUS.CANCELLED,
+ testStatus: [
+ { status: COLLECTION_JOB_STATUS.CANCELLED },
+ { status: COLLECTION_JOB_STATUS.COMPLETED },
+ { status: COLLECTION_JOB_STATUS.CANCELLED }
+ ]
+ }
+ }
+ ];
+
+ const mocks = getMocks(testPlanRuns);
+
+ const { getByText } = render(
+
+
+
+ );
+
+ await waitFor(async () => {
+ // Wait for the component to update
+ // Imperfect but prevents needing to detect loading removal
+ await setTimeout(() => {
+ expect(getByText('1 Test Running')).toBeInTheDocument();
+ expect(getByText('2 Tests Completed')).toBeInTheDocument();
+ expect(getByText('1 Test Queued')).toBeInTheDocument();
+ expect(getByText('2 Tests Cancelled')).toBeInTheDocument();
+ }, 500);
+ });
});
diff --git a/client/tests/DataManagement.test.jsx b/client/tests/DataManagement.test.jsx
index 73a46fadb..c379eea11 100644
--- a/client/tests/DataManagement.test.jsx
+++ b/client/tests/DataManagement.test.jsx
@@ -2,300 +2,292 @@
* @jest-environment jsdom
*/
-import React from 'react';
+import React, { act } from 'react';
import { render, renderHook, 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 '@testing-library/jest-dom';
import DataManagement from '../components/DataManagement';
// eslint-disable-next-line jest/no-mocks-import
import { DATA_MANAGEMENT_PAGE_POPULATED_MOCK_DATA } from './__mocks__/GraphQLMocks';
-import { act } from 'react-dom/test-utils';
import {
- useDataManagementTableFiltering,
- useDataManagementTableSorting,
- useDerivedActivePhasesByTestPlanId,
- useTestPlanVersionsByPhase,
- useTestPlansByPhase
+ useDataManagementTableFiltering,
+ useDataManagementTableSorting,
+ useDerivedActivePhasesByTestPlanId,
+ useTestPlanVersionsByPhase,
+ useTestPlansByPhase
} from '../components/DataManagement/filterSortHooks';
import {
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS,
- DATA_MANAGEMENT_TABLE_SORT_OPTIONS
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS,
+ DATA_MANAGEMENT_TABLE_SORT_OPTIONS
} from '../components/DataManagement/utils';
import { TABLE_SORT_ORDERS } from '../components/common/SortableTableHeader';
import { AriaLiveRegionProvider } from '../components/providers/AriaLiveRegionProvider';
const setup = (mocks = []) => {
- return render(
-
-
-
-
-
-
-
- );
+ return render(
+
+
+
+
+
+
+
+ );
};
describe('Data Management page', () => {
- let wrapper;
+ let wrapper;
- beforeEach(() => {
- wrapper = setup(DATA_MANAGEMENT_PAGE_POPULATED_MOCK_DATA);
- });
+ beforeEach(() => {
+ wrapper = setup(DATA_MANAGEMENT_PAGE_POPULATED_MOCK_DATA);
+ });
- it('renders loading state on initialization', async () => {
- const { getByTestId } = wrapper;
- const element = getByTestId('page-status');
+ it('renders loading state on initialization', async () => {
+ const { getByTestId } = wrapper;
+ const element = getByTestId('page-status');
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent('Loading');
- });
+ 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)));
+ 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);
+ 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);
- });
+ 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);
+ });
});
const testPlans = [
- { title: 'Test A', directory: 'dirA', id: '1' },
- { title: 'Test B', directory: 'dirB', id: '2' },
- { title: 'Test C', directory: 'dirC', id: '3' },
- { title: 'Test D', directory: 'dirD', id: '4' }
+ { title: 'Test A', directory: 'dirA', id: '1' },
+ { title: 'Test B', directory: 'dirB', id: '2' },
+ { title: 'Test C', directory: 'dirC', id: '3' },
+ { title: 'Test D', directory: 'dirD', id: '4' }
];
const testPlanVersions = [
- {
- phase: 'RD',
- id: '101',
- testPlan: { directory: 'dirA' },
- updatedAt: '2022-03-17T18:34:51.000Z'
- },
- {
- phase: 'DRAFT',
- id: '102',
- testPlan: { directory: 'dirB' },
- draftStatusReachedAt: '2022-05-18T20:51:40.000Z'
- },
- {
- phase: 'CANDIDATE',
- id: '103',
- testPlan: { directory: 'dirC' },
- candidatePhaseReachedAt: '2022-04-10T00:00:00.000Z'
- },
- {
- phase: 'RD',
- id: '104',
- testPlan: { directory: 'dirD' },
- updatedAt: '2022-03-18T18:34:51.000Z'
- },
- {
- phase: 'RECOMMENDED',
- id: '105',
- testPlan: { directory: 'dirD' },
- recommendedPhaseReachedAt: '2022-05-18T20:51:40.000Z'
- },
- {
- phase: 'DRAFT',
- id: '106',
- testPlan: { directory: 'dirD' },
- draftStatusReachedAt: '2024-01-08T20:51:40.000Z'
- }
+ {
+ phase: 'RD',
+ id: '101',
+ testPlan: { directory: 'dirA' },
+ updatedAt: '2022-03-17T18:34:51.000Z'
+ },
+ {
+ phase: 'DRAFT',
+ id: '102',
+ testPlan: { directory: 'dirB' },
+ draftStatusReachedAt: '2022-05-18T20:51:40.000Z'
+ },
+ {
+ phase: 'CANDIDATE',
+ id: '103',
+ testPlan: { directory: 'dirC' },
+ candidatePhaseReachedAt: '2022-04-10T00:00:00.000Z'
+ },
+ {
+ phase: 'RD',
+ id: '104',
+ testPlan: { directory: 'dirD' },
+ updatedAt: '2022-03-18T18:34:51.000Z'
+ },
+ {
+ phase: 'RECOMMENDED',
+ id: '105',
+ testPlan: { directory: 'dirD' },
+ recommendedPhaseReachedAt: '2022-05-18T20:51:40.000Z'
+ },
+ {
+ phase: 'DRAFT',
+ id: '106',
+ testPlan: { directory: 'dirD' },
+ draftStatusReachedAt: '2024-01-08T20:51:40.000Z'
+ }
];
const ats = []; // ATs are stubbed until this model is defined
describe('useDataManagementTableSorting hook', () => {
- it('sorts by phase by default', () => {
- const { result } = renderHook(() =>
- useDataManagementTableSorting(
- testPlans,
- testPlanVersions,
- ats,
- TABLE_SORT_ORDERS.DESC
- )
- );
- expect(result.current.sortedTestPlans).toEqual([
- testPlans[3], // RECOMMENDED
- testPlans[2], // CANDIDATE
- testPlans[1], // DRAFT
- testPlans[0] // RD
- ]);
- });
+ it('sorts by phase by default', () => {
+ const { result } = renderHook(() =>
+ useDataManagementTableSorting(
+ testPlans,
+ testPlanVersions,
+ ats,
+ TABLE_SORT_ORDERS.DESC
+ )
+ );
+ expect(result.current.sortedTestPlans).toEqual([
+ testPlans[3], // RECOMMENDED
+ testPlans[2], // CANDIDATE
+ testPlans[1], // DRAFT
+ testPlans[0] // RD
+ ]);
+ });
- it('can sort by name', () => {
- const { result } = renderHook(() =>
- useDataManagementTableSorting(testPlans, testPlanVersions, ats)
- );
- act(() =>
- result.current.updateSort({
- key: DATA_MANAGEMENT_TABLE_SORT_OPTIONS.NAME,
- direction: TABLE_SORT_ORDERS.ASC
- })
- );
- expect(result.current.sortedTestPlans).toEqual(testPlans);
- });
+ it('can sort by name', () => {
+ const { result } = renderHook(() =>
+ useDataManagementTableSorting(testPlans, testPlanVersions, ats)
+ );
+ act(() =>
+ result.current.updateSort({
+ key: DATA_MANAGEMENT_TABLE_SORT_OPTIONS.NAME,
+ direction: TABLE_SORT_ORDERS.ASC
+ })
+ );
+ expect(result.current.sortedTestPlans).toEqual(testPlans);
+ });
});
describe('useDataManagementTableFiltering hook', () => {
- it('shows all plans by default', () => {
- const { result } = renderHook(() =>
- useDataManagementTableFiltering(
- testPlans,
- testPlanVersions,
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL
- )
- );
- expect(result.current.filteredTestPlans).toEqual(testPlans);
- expect(
- result.current.filterLabels[
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL
- ]
- ).toEqual(`All Plans (${testPlans.length})`);
- });
+ it('shows all plans by default', () => {
+ const { result } = renderHook(() =>
+ useDataManagementTableFiltering(
+ testPlans,
+ testPlanVersions,
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL
+ )
+ );
+ expect(result.current.filteredTestPlans).toEqual(testPlans);
+ expect(
+ result.current.filterLabels[DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL]
+ ).toEqual(`All Plans (${testPlans.length})`);
+ });
- it('can filter by RD phase', () => {
- const { result } = renderHook(() =>
- useDataManagementTableFiltering(
- testPlans,
- testPlanVersions,
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RD
- )
- );
- expect(result.current.filteredTestPlans).toEqual([
- testPlans[0], // RD
- testPlans[3]
- ]);
- expect(
- result.current.filterLabels[DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RD]
- ).toEqual(`R&D Complete (2)`);
- });
+ it('can filter by RD phase', () => {
+ const { result } = renderHook(() =>
+ useDataManagementTableFiltering(
+ testPlans,
+ testPlanVersions,
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RD
+ )
+ );
+ expect(result.current.filteredTestPlans).toEqual([
+ testPlans[0], // RD
+ testPlans[3]
+ ]);
+ expect(
+ result.current.filterLabels[DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RD]
+ ).toEqual(`R&D Complete (2)`);
+ });
- it('can filter by DRAFT phase', () => {
- const { result } = renderHook(() =>
- useDataManagementTableFiltering(
- testPlans,
- testPlanVersions,
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.DRAFT
- )
- );
- expect(result.current.filteredTestPlans).toEqual([
- testPlans[1], // DRAFT
- testPlans[3] // DRAFT
- ]);
- expect(
- result.current.filterLabels[
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.DRAFT
- ]
- ).toEqual(`In Draft Review (2)`); // Test plan 106 is in DRAFT while the overall plan is RECOMMENDED
- });
+ it('can filter by DRAFT phase', () => {
+ const { result } = renderHook(() =>
+ useDataManagementTableFiltering(
+ testPlans,
+ testPlanVersions,
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.DRAFT
+ )
+ );
+ expect(result.current.filteredTestPlans).toEqual([
+ testPlans[1], // DRAFT
+ testPlans[3] // DRAFT
+ ]);
+ expect(
+ result.current.filterLabels[DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.DRAFT]
+ ).toEqual(`In Draft Review (2)`); // Test plan 106 is in DRAFT while the overall plan is RECOMMENDED
+ });
- it('can filter by CANDIDATE phase', () => {
- const { result } = renderHook(() =>
- useDataManagementTableFiltering(
- testPlans,
- testPlanVersions,
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.CANDIDATE
- )
- );
- expect(result.current.filteredTestPlans).toEqual([
- testPlans[2] // CANDIDATE
- ]);
- expect(
- result.current.filterLabels[
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.CANDIDATE
- ]
- ).toEqual(`In Candidate Review (1)`);
- });
+ it('can filter by CANDIDATE phase', () => {
+ const { result } = renderHook(() =>
+ useDataManagementTableFiltering(
+ testPlans,
+ testPlanVersions,
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.CANDIDATE
+ )
+ );
+ expect(result.current.filteredTestPlans).toEqual([
+ testPlans[2] // CANDIDATE
+ ]);
+ expect(
+ result.current.filterLabels[
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.CANDIDATE
+ ]
+ ).toEqual(`In Candidate Review (1)`);
+ });
- it('can filter by RECOMMENDED phase', () => {
- const { result } = renderHook(() =>
- useDataManagementTableFiltering(
- testPlans,
- testPlanVersions,
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED
- )
- );
- expect(result.current.filteredTestPlans).toEqual([
- testPlans[3] // RECOMMENDED
- ]);
- expect(
- result.current.filterLabels[
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED
- ]
- ).toEqual(`Recommended Plans (1)`);
- });
+ it('can filter by RECOMMENDED phase', () => {
+ const { result } = renderHook(() =>
+ useDataManagementTableFiltering(
+ testPlans,
+ testPlanVersions,
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED
+ )
+ );
+ expect(result.current.filteredTestPlans).toEqual([
+ testPlans[3] // RECOMMENDED
+ ]);
+ expect(
+ result.current.filterLabels[
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED
+ ]
+ ).toEqual(`Recommended Plans (1)`);
+ });
});
describe('useTestPlanVersionsByPhase hook', () => {
- it('returns an object with test plan versions grouped by phase', () => {
- const { result } = renderHook(() =>
- useTestPlanVersionsByPhase(testPlanVersions)
- );
- const { testPlanVersionsByPhase } = result.current;
- expect(testPlanVersionsByPhase).toEqual({
- RD: [testPlanVersions[0], testPlanVersions[3]],
- DRAFT: [testPlanVersions[1], testPlanVersions[5]],
- CANDIDATE: [testPlanVersions[2]],
- RECOMMENDED: [testPlanVersions[4]]
- });
+ it('returns an object with test plan versions grouped by phase', () => {
+ const { result } = renderHook(() =>
+ useTestPlanVersionsByPhase(testPlanVersions)
+ );
+ const { testPlanVersionsByPhase } = result.current;
+ expect(testPlanVersionsByPhase).toEqual({
+ RD: [testPlanVersions[0], testPlanVersions[3]],
+ DRAFT: [testPlanVersions[1], testPlanVersions[5]],
+ CANDIDATE: [testPlanVersions[2]],
+ RECOMMENDED: [testPlanVersions[4]]
});
+ });
});
describe('useDerivedTestPlanOverallPhase hook', () => {
- it('returns an object with the active phases mapped to each test plan id', () => {
- const { result } = renderHook(() =>
- useDerivedActivePhasesByTestPlanId(testPlans, testPlanVersions)
- );
- const { derivedActivePhasesByTestPlanId } = result.current;
- expect(derivedActivePhasesByTestPlanId).toEqual({
- 1: ['RD'],
- 2: ['DRAFT'],
- 3: ['CANDIDATE'],
- 4: ['RECOMMENDED', 'DRAFT', 'RD']
- });
+ it('returns an object with the active phases mapped to each test plan id', () => {
+ const { result } = renderHook(() =>
+ useDerivedActivePhasesByTestPlanId(testPlans, testPlanVersions)
+ );
+ const { derivedActivePhasesByTestPlanId } = result.current;
+ expect(derivedActivePhasesByTestPlanId).toEqual({
+ 1: ['RD'],
+ 2: ['DRAFT'],
+ 3: ['CANDIDATE'],
+ 4: ['RECOMMENDED', 'DRAFT', 'RD']
});
+ });
});
describe('useTestPlansByPhase hook', () => {
- it('returns an object with test plans with an array of active Test Plan Versions', () => {
- const { result } = renderHook(() =>
- useTestPlansByPhase(testPlans, testPlanVersions)
- );
- const { testPlansByPhase } = result.current;
- expect(testPlansByPhase).toEqual({
- RD: [testPlans[0], testPlans[3]],
- DRAFT: [testPlans[1], testPlans[3]],
- CANDIDATE: [testPlans[2]],
- RECOMMENDED: [testPlans[3]]
- });
+ it('returns an object with test plans with an array of active Test Plan Versions', () => {
+ const { result } = renderHook(() =>
+ useTestPlansByPhase(testPlans, testPlanVersions)
+ );
+ const { testPlansByPhase } = result.current;
+ expect(testPlansByPhase).toEqual({
+ RD: [testPlans[0], testPlans[3]],
+ DRAFT: [testPlans[1], testPlans[3]],
+ CANDIDATE: [testPlans[2]],
+ RECOMMENDED: [testPlans[3]]
});
+ });
});
diff --git a/client/tests/FilterButtons.test.jsx b/client/tests/FilterButtons.test.jsx
index 26a43cc63..8edf3d32d 100644
--- a/client/tests/FilterButtons.test.jsx
+++ b/client/tests/FilterButtons.test.jsx
@@ -9,54 +9,52 @@ import FilterButtons from '../components/common/FilterButtons';
import { DATA_MANAGEMENT_TABLE_FILTER_OPTIONS } from '../components/DataManagement/utils';
describe('FilterButtons', () => {
- const filterOptions = {
- [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RD]: `R&D Complete`,
- [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.DRAFT]: `In Draft Review`,
- [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.CANDIDATE]: `In Candidate Review`,
- [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED]: `Recommended Plans`,
- [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL]: `All Plans`
- };
- const defaultProps = {
- filterOptions,
- filterLabel: 'Filter',
- activeFilter: DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL,
- onFilterChange: () => {}
- };
+ const filterOptions = {
+ [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RD]: `R&D Complete`,
+ [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.DRAFT]: `In Draft Review`,
+ [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.CANDIDATE]: `In Candidate Review`,
+ [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED]: `Recommended Plans`,
+ [DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL]: `All Plans`
+ };
+ const defaultProps = {
+ filterOptions,
+ filterLabel: 'Filter',
+ activeFilter: DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL,
+ onFilterChange: () => {}
+ };
- it('should render without crashing', () => {
- render( );
- expect(screen.getByRole('group')).toBeInTheDocument();
- });
+ it('should render without crashing', () => {
+ render( );
+ expect(screen.getByRole('group')).toBeInTheDocument();
+ });
- it('should render the correct filter labels', () => {
- render( );
- Object.values(filterOptions).forEach(label => {
- expect(screen.getByText(label)).toBeInTheDocument();
- });
+ it('should render the correct filter labels', () => {
+ render( );
+ Object.values(filterOptions).forEach(label => {
+ expect(screen.getByText(label)).toBeInTheDocument();
});
+ });
- it('should render the active filter with correct styles', () => {
- render( );
- const activeButton = screen
- .getByText(filterOptions[DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL])
- .closest('button');
- expect(activeButton).toHaveAttribute('aria-pressed', 'true');
- expect(activeButton).toHaveClass('active');
- });
+ it('should render the active filter with correct styles', () => {
+ render( );
+ const activeButton = screen
+ .getByText(filterOptions[DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.ALL])
+ .closest('button');
+ expect(activeButton).toHaveAttribute('aria-pressed', 'true');
+ expect(activeButton).toHaveClass('active');
+ });
- it('should change filter on button click', () => {
- const onFilterChange = jest.fn();
- render(
-
- );
- const button = screen
- .getByText(
- filterOptions[DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED]
- )
- .closest('button');
- fireEvent.click(button);
- expect(onFilterChange).toHaveBeenCalledWith(
- DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED
- );
- });
+ it('should change filter on button click', () => {
+ const onFilterChange = jest.fn();
+ render( );
+ const button = screen
+ .getByText(
+ filterOptions[DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED]
+ )
+ .closest('button');
+ fireEvent.click(button);
+ expect(onFilterChange).toHaveBeenCalledWith(
+ DATA_MANAGEMENT_TABLE_FILTER_OPTIONS.RECOMMENDED
+ );
+ });
});
diff --git a/client/tests/FocusTrapper.test.jsx b/client/tests/FocusTrapper.test.jsx
index a4c114d85..aa252755d 100644
--- a/client/tests/FocusTrapper.test.jsx
+++ b/client/tests/FocusTrapper.test.jsx
@@ -6,120 +6,116 @@ import { render, fireEvent, act } from '@testing-library/react';
import FocusTrapper from '../components/common/FocusTrapper';
describe('FocusTrapper', () => {
- let trappedDiv, initialFocusRef;
-
- beforeEach(() => {
- trappedDiv = document.createElement('div');
- trappedDiv.id = 'trapped-div';
- document.body.appendChild(trappedDiv);
- });
-
- afterEach(() => {
- document.body.removeChild(trappedDiv);
+ let trappedDiv, initialFocusRef;
+
+ beforeEach(() => {
+ trappedDiv = document.createElement('div');
+ trappedDiv.id = 'trapped-div';
+ document.body.appendChild(trappedDiv);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(trappedDiv);
+ });
+
+ const renderEls = async () => {
+ initialFocusRef = React.createRef();
+ return render(
+
+
+ Modal Header
+
+ Click Me
+ Link
+ ,
+ { container: document.body.appendChild(trappedDiv) }
+ );
+ };
+
+ it('should identify focusable elements', async () => {
+ await renderEls();
+
+ const focusables = trappedDiv.querySelectorAll(
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
+ );
+
+ // Two original focusable elements plus 2 for before and after trap
+ expect(focusables.length).toBe(4);
+ });
+
+ it('should trap focus and allow forward navigation when isActive is true', async () => {
+ await renderEls();
+
+ const container = document.getElementById('trapped-div');
+
+ const firstFocusable = container.querySelector('button');
+ const lastFocusable = container.querySelector('a');
+
+ act(() => {
+ lastFocusable.focus();
});
- const renderEls = async () => {
- initialFocusRef = React.createRef();
- return render(
-
-
- Modal Header
-
- Click Me
- Link
- ,
- { container: document.body.appendChild(trappedDiv) }
- );
- };
-
- it('should identify focusable elements', async () => {
- await renderEls();
-
- const focusables = trappedDiv.querySelectorAll(
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
- );
-
- // Two original focusable elements plus 2 for before and after trap
- expect(focusables.length).toBe(4);
+ act(() => {
+ fireEvent.keyDown(container, {
+ key: 'Tab',
+ code: 9,
+ shiftKey: false
+ });
});
- it('should trap focus and allow forward navigation when isActive is true', async () => {
- await renderEls();
-
- const container = document.getElementById('trapped-div');
+ expect(document.activeElement).toBe(firstFocusable);
+ });
- const firstFocusable = container.querySelector('button');
- const lastFocusable = container.querySelector('a');
+ it('should trap focus when and allow backward navigation when isActive is true', async () => {
+ await renderEls();
- act(() => {
- lastFocusable.focus();
- });
+ const container = document.getElementById('trapped-div');
- act(() => {
- fireEvent.keyDown(container, {
- key: 'Tab',
- code: 9,
- shiftKey: false
- });
- });
+ const lastFocusable = container.querySelector('a');
- expect(document.activeElement).toBe(firstFocusable);
+ act(() => {
+ initialFocusRef.current.focus();
});
- it('should trap focus when and allow backward navigation when isActive is true', async () => {
- await renderEls();
-
- const container = document.getElementById('trapped-div');
+ act(() => {
+ fireEvent.keyDown(container, {
+ key: 'Tab',
+ code: 9,
+ shiftKey: true
+ });
+ });
- const lastFocusable = container.querySelector('a');
+ expect(document.activeElement).toBe(lastFocusable);
+ });
- act(() => {
- initialFocusRef.current.focus();
- });
+ it('should not trap focus when isActive is false', async () => {
+ const { container } = render(
+
+ Click Me
+
+ ,
+ { container: document.body.appendChild(trappedDiv) }
+ );
- act(() => {
- fireEvent.keyDown(container, {
- key: 'Tab',
- code: 9,
- shiftKey: true
- });
- });
+ const firstFocusable = container.querySelector('button');
+ const lastFocusable = container.querySelector('input');
- expect(document.activeElement).toBe(lastFocusable);
+ act(() => {
+ firstFocusable.focus();
});
- it('should not trap focus when isActive is false', async () => {
- const { container } = render(
-
- Click Me
-
- ,
- { container: document.body.appendChild(trappedDiv) }
- );
-
- const firstFocusable = container.querySelector('button');
- const lastFocusable = container.querySelector('input');
-
- act(() => {
- firstFocusable.focus();
- });
-
- act(() => {
- fireEvent.keyDown(container, {
- key: 'Tab',
- code: 9,
- shiftKey: true
- });
- });
-
- expect(document.activeElement).not.toBe(lastFocusable);
+ act(() => {
+ fireEvent.keyDown(container, {
+ key: 'Tab',
+ code: 9,
+ shiftKey: true
+ });
});
+
+ expect(document.activeElement).not.toBe(lastFocusable);
+ });
});
diff --git a/client/tests/SortableTableHeader.test.jsx b/client/tests/SortableTableHeader.test.jsx
index 84829dcef..37a2bfa1e 100644
--- a/client/tests/SortableTableHeader.test.jsx
+++ b/client/tests/SortableTableHeader.test.jsx
@@ -5,71 +5,71 @@
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import SortableTableHeader, {
- TABLE_SORT_ORDERS
+ TABLE_SORT_ORDERS
} from '../components/common/SortableTableHeader';
import '@testing-library/jest-dom';
import { AriaLiveRegionProvider } from '../components/providers/AriaLiveRegionProvider';
const renderComponent = props =>
- render(
-
-
-
- );
+ render(
+
+
+
+ );
const getAriaSort = (active, sortOrder) =>
- active
- ? sortOrder === TABLE_SORT_ORDERS.ASC
- ? 'ascending'
- : 'descending'
- : 'none';
+ active
+ ? sortOrder === TABLE_SORT_ORDERS.ASC
+ ? 'ascending'
+ : 'descending'
+ : 'none';
describe('SortableTableHeader component', () => {
- const defaultProps = { title: 'Header', active: false, onSort: () => {} };
+ const defaultProps = { title: 'Header', active: false, onSort: () => {} };
- it('should render without crashing', () => {
- renderComponent(defaultProps);
- expect(screen.getByRole('columnheader')).toBeInTheDocument();
- });
+ it('should render without crashing', () => {
+ renderComponent(defaultProps);
+ expect(screen.getByRole('columnheader')).toBeInTheDocument();
+ });
- it('should render the correct title', () => {
- renderComponent(defaultProps);
- expect(screen.getByText('Header')).toBeInTheDocument();
- });
+ it('should render the correct title', () => {
+ renderComponent(defaultProps);
+ expect(screen.getByText('Header')).toBeInTheDocument();
+ });
- it('should render the inactive icon when active is false', () => {
- renderComponent(defaultProps);
- expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument();
- });
+ it('should render the inactive icon when active is false', () => {
+ renderComponent(defaultProps);
+ expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument();
+ });
- it('should handle sorting order and aria-sort attribute when active is true and clicked', () => {
- const onSort = jest.fn();
- renderComponent({ ...defaultProps, active: true, onSort });
- const button = screen.getByRole('button');
+ it('should handle sorting order and aria-sort attribute when active is true and clicked', () => {
+ const onSort = jest.fn();
+ renderComponent({ ...defaultProps, active: true, onSort });
+ const button = screen.getByRole('button');
- expect(screen.getByRole('columnheader')).toHaveAttribute(
- 'aria-sort',
- getAriaSort(true, TABLE_SORT_ORDERS.ASC)
- );
+ expect(screen.getByRole('columnheader')).toHaveAttribute(
+ 'aria-sort',
+ getAriaSort(true, TABLE_SORT_ORDERS.ASC)
+ );
- fireEvent.click(button);
- expect(onSort).toHaveBeenCalledWith(TABLE_SORT_ORDERS.DESC);
- expect(screen.getByRole('columnheader')).toHaveAttribute(
- 'aria-sort',
- getAriaSort(true, TABLE_SORT_ORDERS.DESC)
- );
+ fireEvent.click(button);
+ expect(onSort).toHaveBeenCalledWith(TABLE_SORT_ORDERS.DESC);
+ expect(screen.getByRole('columnheader')).toHaveAttribute(
+ 'aria-sort',
+ getAriaSort(true, TABLE_SORT_ORDERS.DESC)
+ );
- fireEvent.click(button);
- expect(onSort).toHaveBeenCalledWith(TABLE_SORT_ORDERS.ASC);
- expect(screen.getByRole('columnheader')).toHaveAttribute(
- 'aria-sort',
- getAriaSort(true, TABLE_SORT_ORDERS.ASC)
- );
- });
+ fireEvent.click(button);
+ expect(onSort).toHaveBeenCalledWith(TABLE_SORT_ORDERS.ASC);
+ expect(screen.getByRole('columnheader')).toHaveAttribute(
+ 'aria-sort',
+ getAriaSort(true, TABLE_SORT_ORDERS.ASC)
+ );
+ });
});
diff --git a/client/tests/TestPlanReportStatusDialog.test.jsx b/client/tests/TestPlanReportStatusDialog.test.jsx
index 5e3064ccf..c542a01c3 100644
--- a/client/tests/TestPlanReportStatusDialog.test.jsx
+++ b/client/tests/TestPlanReportStatusDialog.test.jsx
@@ -16,61 +16,59 @@ import { TEST_PLAN_REPORT_STATUS_DIALOG_MOCK_DATA } from './__mocks__/GraphQLMoc
import { mockedTestPlanVersion } from './__mocks__/GraphQLMocks/TestPlanReportStatusDialogMock';
const setup = (props, mocks = []) => {
- return render(
-
-
-
-
-
- );
+ return render(
+
+
+
+
+
+ );
};
describe('TestPlanReportStatusDialog', () => {
- let getByRole, getByText;
+ let getByRole, getByText;
- beforeEach(() => {
- const show = true;
- const handleHide = jest.fn();
- const testPlanVersion = mockedTestPlanVersion;
- const [
- ,
- {
- result: {
- data: { ats }
- }
- }
- ] = TEST_PLAN_REPORT_STATUS_DIALOG_MOCK_DATA;
+ beforeEach(() => {
+ const show = true;
+ const handleHide = jest.fn();
+ const testPlanVersion = mockedTestPlanVersion;
+ const [
+ ,
+ {
+ result: {
+ data: { ats }
+ }
+ }
+ ] = TEST_PLAN_REPORT_STATUS_DIALOG_MOCK_DATA;
- const result = setup(
- { testPlanVersion, show, ats, handleHide },
- TEST_PLAN_REPORT_STATUS_DIALOG_MOCK_DATA
- );
+ const result = setup(
+ { testPlanVersion, show, ats, handleHide },
+ TEST_PLAN_REPORT_STATUS_DIALOG_MOCK_DATA
+ );
- getByRole = result.getByRole;
- getByText = result.getByText;
- });
+ getByRole = result.getByRole;
+ getByText = result.getByText;
+ });
- test('renders without error', async () => {
- await waitFor(() => expect(getByRole('dialog')).toBeInTheDocument());
- });
+ test('renders without error', async () => {
+ await waitFor(() => expect(getByRole('dialog')).toBeInTheDocument());
+ });
- test('displays the dialog title', async () => {
- await waitFor(() => {
- expect(
- getByText('Report Status for the Test Plan')
- ).toBeInTheDocument();
- });
+ test('displays the dialog title', async () => {
+ await waitFor(() => {
+ expect(getByText('Report Status for the Test Plan')).toBeInTheDocument();
});
+ });
- test('displays the table headers', async () => {
- await waitFor(() => {
- expect(getByText('Required')).toBeInTheDocument();
- expect(getByText('AT')).toBeInTheDocument();
- expect(getByText('Browser')).toBeInTheDocument();
- expect(getByText('Report Status')).toBeInTheDocument();
- });
+ test('displays the table headers', async () => {
+ await waitFor(() => {
+ expect(getByText('Required')).toBeInTheDocument();
+ expect(getByText('AT')).toBeInTheDocument();
+ expect(getByText('Browser')).toBeInTheDocument();
+ expect(getByText('Report Status')).toBeInTheDocument();
});
+ });
});
diff --git a/client/tests/TestQueue.test.jsx b/client/tests/TestQueue.test.jsx
index f349ae3bd..d64af01f9 100644
--- a/client/tests/TestQueue.test.jsx
+++ b/client/tests/TestQueue.test.jsx
@@ -3,320 +3,187 @@
*/
import React from 'react';
-import { render, screen, waitFor, fireEvent } from '@testing-library/react';
+import { render, waitFor, fireEvent } 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 '@testing-library/jest-dom';
import TestQueue from '../components/TestQueue';
// eslint-disable-next-line jest/no-mocks-import
import {
- TEST_QUEUE_PAGE_ADMIN_NOT_POPULATED_MOCK_DATA,
- TEST_QUEUE_PAGE_TESTER_NOT_POPULATED_MOCK_DATA,
- TEST_QUEUE_PAGE_ADMIN_POPULATED_MOCK_DATA,
- TEST_QUEUE_PAGE_TESTER_POPULATED_MOCK_DATA,
- TEST_QUEUE_PAGE_BASE_MOCK_DATA
+ TEST_QUEUE_PAGE_ADMIN_NOT_POPULATED_MOCK_DATA,
+ TEST_QUEUE_PAGE_TESTER_NOT_POPULATED_MOCK_DATA,
+ TEST_QUEUE_PAGE_BASE_MOCK_DATA
} from './__mocks__/GraphQLMocks';
import { AriaLiveRegionProvider } from '../components/providers/AriaLiveRegionProvider';
const setup = (mocks = []) => {
- const mergedMocks = [...TEST_QUEUE_PAGE_BASE_MOCK_DATA, ...mocks];
- return render(
-
-
-
-
-
-
-
- );
+ const mergedMocks = [...TEST_QUEUE_PAGE_BASE_MOCK_DATA, ...mocks];
+ return render(
+
+
+
+
+
+
+
+ );
};
describe('Render TestQueue/index.jsx', () => {
- let wrapper;
+ let wrapper;
- describe('[NOT ADMIN] when no test plan reports exist', () => {
- beforeEach(() => {
- wrapper = setup(TEST_QUEUE_PAGE_TESTER_NOT_POPULATED_MOCK_DATA);
- });
-
- it('renders loading state on initialization', async () => {
- const { getByTestId } = wrapper;
- const element = getByTestId('page-status');
-
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent('Loading');
- });
-
- // TODO: Revise timeout with pageReady check
- it.skip('renders Test Queue page instructions', async () => {
- // allow page time to load
-
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryByTestId, getByTestId } = wrapper;
- const loadingElement = queryByTestId('page-status');
- const element = getByTestId('test-queue-no-test-plans-p');
-
- expect(loadingElement).not.toBeInTheDocument();
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent(
- /Please configure your preferred Assistive Technologies in/i
- );
- });
-
- it('renders no AT-specific sections', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryAllByText } = wrapper;
- const nvdaElements = queryAllByText(/nvda/i);
- const jawsElements = queryAllByText(/jaws/i);
- const voiceOverElements = queryAllByText(/voiceover/i);
-
- expect(nvdaElements.length).toEqual(0);
- expect(jawsElements.length).toEqual(0);
- expect(voiceOverElements.length).toEqual(0);
- });
-
- it('does not render add test plan modal button', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryByTestId } = wrapper;
- const button = queryByTestId(
- 'test-queue-add-test-plan-to-queue-button'
- );
-
- expect(button).not.toBeTruthy();
- });
+ describe('not admin when no test plan reports exist', () => {
+ beforeEach(() => {
+ wrapper = setup(TEST_QUEUE_PAGE_TESTER_NOT_POPULATED_MOCK_DATA);
});
- describe('[NOT ADMIN] when test plan reports exist', () => {
- beforeEach(() => {
- wrapper = setup(TEST_QUEUE_PAGE_TESTER_POPULATED_MOCK_DATA);
- });
-
- it('renders loading state on initialization', async () => {
- const { getByTestId } = wrapper;
- const element = getByTestId('page-status');
-
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent('Loading');
- });
-
- it.skip('renders Test Queue page instructions', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryByTestId, getByTestId } = wrapper;
- const loadingElement = queryByTestId('page-status');
- const element = getByTestId('test-queue-instructions');
-
- expect(loadingElement).not.toBeInTheDocument();
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent(
- 'Assign yourself a test plan or start executing one that is already assigned to you.'
- );
- });
-
- it('renders AT-specific sections', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryAllByText } = wrapper;
- const nvdaElements = queryAllByText(/nvda/i);
- const jawsElements = queryAllByText(/jaws/i);
- const voiceOverElements = queryAllByText(/voiceover/i);
-
- expect(nvdaElements.length).toBeGreaterThanOrEqual(1);
- expect(jawsElements.length).toBeGreaterThanOrEqual(1);
- expect(voiceOverElements.length).toBeGreaterThanOrEqual(1);
- });
-
- it('renders testers are assigned to Test Plans', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryAllByText } = wrapper;
- const userAAssignedElements = queryAllByText(/foo-bar/i);
- const userBAssignedElements = queryAllByText(/bar-foo/i);
- const userCAssignedElements = queryAllByText(/boo-far/i);
- const assignedTestsElements = queryAllByText(
- /\d+ of \d+ tests complete/i
- );
-
- expect(userAAssignedElements.length).toBeGreaterThanOrEqual(1);
- expect(userBAssignedElements.length).toBeGreaterThanOrEqual(1);
- expect(userCAssignedElements.length).toBeGreaterThanOrEqual(1);
- expect(assignedTestsElements.length).toBeGreaterThanOrEqual(1);
- });
-
- it('does not render add test plan modal button', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryByTestId } = wrapper;
- const button = queryByTestId(
- 'test-queue-add-test-plan-to-queue-button'
- );
+ it('renders loading state on initialization', async () => {
+ const { queryByTestId } = wrapper;
+ const element = queryByTestId('page-status');
- expect(button).not.toBeTruthy();
- });
+ expect(element).toBeTruthy();
+ expect(element).toHaveTextContent('Loading');
});
- describe('[IS ADMIN] when no test plan reports exist', () => {
- beforeEach(() => {
- wrapper = setup(TEST_QUEUE_PAGE_ADMIN_NOT_POPULATED_MOCK_DATA);
- });
-
- it('renders loading state on initialization', async () => {
- const { getByTestId } = wrapper;
- const element = getByTestId('page-status');
-
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent('Loading');
- });
-
- it('renders Test Queue page instructions', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryByTestId, getByTestId } = wrapper;
- const loadingElement = queryByTestId('page-status');
- const element = getByTestId('test-queue-no-test-plans-p');
-
- expect(loadingElement).not.toBeInTheDocument();
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent(/Add a Test Plan to the Queue/i);
- });
-
- it('renders no AT-specific sections', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryAllByText } = wrapper;
- const nvdaElements = queryAllByText(/nvda/i);
- const jawsElements = queryAllByText(/jaws/i);
- const voiceOverElements = queryAllByText(/voiceover/i);
-
- expect(nvdaElements.length).toEqual(0);
- expect(jawsElements.length).toEqual(0);
- expect(voiceOverElements.length).toEqual(0);
- });
-
- it.skip('renders add test plan modal on button click', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
-
- const { queryByTestId } = wrapper;
- const button = queryByTestId(
- 'test-queue-add-test-plan-to-queue-button'
- );
-
- expect(button).toBeTruthy();
-
- // opens modal
- fireEvent.click(button);
-
- expect(
- screen.getByText('Select an AT and Version')
- ).toBeInTheDocument();
- expect(
- screen.getByText('Select a Browser and Version')
- ).toBeInTheDocument();
- expect(
- screen.getByText('Select a Test Plan and Version')
- ).toBeInTheDocument();
- });
+ it('renders Test Queue page instructions', async () => {
+ // allow page time to load
+ await waitFor(() => new Promise(res => setTimeout(res, 0)));
+
+ const { queryByTestId } = wrapper;
+ const loadingElement = queryByTestId('page-status');
+ const noTestPlansMessageElement = queryByTestId('no-test-plans');
+ const addTestPlanToQueueMessageElement = queryByTestId(
+ 'add-test-plans-queue'
+ );
+
+ expect(loadingElement).not.toBeInTheDocument();
+ expect(noTestPlansMessageElement).toBeTruthy();
+ expect(noTestPlansMessageElement).toHaveTextContent(
+ /There are currently no test plan reports available/i
+ );
+ expect(addTestPlanToQueueMessageElement).not.toBeTruthy();
});
- describe('[IS ADMIN] when test plan reports exist', () => {
- beforeEach(() => {
- wrapper = setup(TEST_QUEUE_PAGE_ADMIN_POPULATED_MOCK_DATA);
- });
-
- it('renders loading state on initialization', async () => {
- const { getByTestId } = wrapper;
- const element = getByTestId('page-status');
-
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent('Loading');
- });
+ it('renders no AT-specific sections', async () => {
+ // allow page time to load
+ await waitFor(() => new Promise(res => setTimeout(res, 0)));
- it.skip('renders Test Queue page instructions', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
+ const { queryAllByText } = wrapper;
+ const nvdaElements = queryAllByText(/nvda/i);
+ const jawsElements = queryAllByText(/jaws/i);
+ const voiceOverElements = queryAllByText(/voiceover/i);
- const { queryByTestId, getByTestId } = wrapper;
- const loadingElement = queryByTestId('page-status');
- const element = getByTestId('test-queue-instructions');
-
- expect(loadingElement).not.toBeInTheDocument();
- expect(element).toBeTruthy();
- expect(element).toHaveTextContent(
- 'Assign yourself a test plan or start executing one that is already assigned to you.'
- );
- });
-
- it('renders AT-specific sections', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
+ expect(nvdaElements.length).toEqual(0);
+ expect(jawsElements.length).toEqual(0);
+ expect(voiceOverElements.length).toEqual(0);
+ });
- const { queryAllByText } = wrapper;
- const nvdaElements = queryAllByText(/nvda/i);
- const jawsElements = queryAllByText(/jaws/i);
- const voiceOverElements = queryAllByText(/voiceover/i);
+ it('does not render ManageTestQueue component', async () => {
+ // allow page time to load
+ await waitFor(() => new Promise(res => setTimeout(res, 0)));
+
+ const { queryByText } = wrapper;
+ expect(
+ queryByText('Manage Assistive Technology Versions')
+ ).not.toBeInTheDocument();
+ expect(
+ queryByText('Add Test Plans to the Test Queue')
+ ).not.toBeInTheDocument();
+ });
+ });
- expect(nvdaElements.length).toBeGreaterThanOrEqual(1);
- expect(jawsElements.length).toBeGreaterThanOrEqual(1);
- expect(voiceOverElements.length).toBeGreaterThanOrEqual(1);
- });
+ describe('is admin when no test plan reports exist', () => {
+ beforeEach(() => {
+ wrapper = setup(TEST_QUEUE_PAGE_ADMIN_NOT_POPULATED_MOCK_DATA);
+ });
- it('renders testers are assigned to Test Plans', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
+ it('renders loading state on initialization', async () => {
+ const { queryByTestId } = wrapper;
+ const element = queryByTestId('page-status');
- const { queryAllByText } = wrapper;
- const userAAssignedElements = queryAllByText(/esmeralda-baggins/i);
- const userBAssignedElements = queryAllByText(/tom-proudfeet/i);
- const assignedTestsElements = queryAllByText(
- /\d+ of \d+ tests complete/i
- );
+ expect(element).toBeTruthy();
+ expect(element).toHaveTextContent('Loading');
+ });
- expect(userAAssignedElements.length).toBeGreaterThanOrEqual(1);
- expect(userBAssignedElements.length).toBeGreaterThanOrEqual(1);
- expect(assignedTestsElements.length).toBeGreaterThanOrEqual(1);
- });
+ it('renders Test Queue page instructions', async () => {
+ // allow page time to load
+ await waitFor(() => new Promise(res => setTimeout(res, 0)));
+
+ const { queryByTestId } = wrapper;
+ const loadingElement = queryByTestId('page-status');
+ const noTestPlansMessageElement = queryByTestId('no-test-plans');
+ const addTestPlanToQueueMessageElement = queryByTestId(
+ 'add-test-plans-queue'
+ );
+
+ expect(loadingElement).not.toBeInTheDocument();
+ expect(noTestPlansMessageElement).toBeTruthy();
+ expect(noTestPlansMessageElement).toHaveTextContent(
+ /There are currently no test plan reports available/i
+ );
+ expect(addTestPlanToQueueMessageElement).toHaveTextContent(
+ /Add a Test Plan to the Queue/i
+ );
+ });
- it.skip('renders add test plan modal on button click', async () => {
- // allow page time to load
- await waitFor(() => new Promise(res => setTimeout(res, 0)));
+ it('renders no AT-specific sections', async () => {
+ // allow page time to load
+ await waitFor(() => new Promise(res => setTimeout(res, 0)));
- const { queryByTestId } = wrapper;
- const button = queryByTestId(
- 'test-queue-add-test-plan-to-queue-button'
- );
+ const { queryAllByText } = wrapper;
+ const nvdaElements = queryAllByText(/nvda/i);
+ const jawsElements = queryAllByText(/jaws/i);
+ const voiceOverElements = queryAllByText(/voiceover/i);
- expect(button).toBeTruthy();
+ expect(nvdaElements.length).toEqual(0);
+ expect(jawsElements.length).toEqual(0);
+ expect(voiceOverElements.length).toEqual(0);
+ });
- // opens modal
- fireEvent.click(button);
+ it('renders ManageTestQueue component', async () => {
+ // allow page time to load
+ await waitFor(() => new Promise(res => setTimeout(res, 0)));
+
+ const { queryByText } = wrapper;
+ expect(
+ queryByText('Manage Assistive Technology Versions')
+ ).toBeInTheDocument();
+ expect(
+ queryByText('Add Test Plans to the Test Queue')
+ ).toBeInTheDocument();
+ });
- expect(
- screen.getByText('Select an AT and Version')
- ).toBeInTheDocument();
- expect(
- screen.getByText('Select a Browser and Version')
- ).toBeInTheDocument();
- expect(
- screen.getByText('Select a Test Plan and Version')
- ).toBeInTheDocument();
- });
+ it('renders add test plan modal on button click', async () => {
+ // allow page time to load
+ await waitFor(() => new Promise(res => setTimeout(res, 0)));
+
+ const { queryByRole, queryByText } = wrapper;
+ const manageAssistiveTechnologyVersionsButton = queryByRole('button', {
+ name: 'Manage Assistive Technology Versions'
+ });
+ const addTestPlansToTestQueueButton = queryByRole('button', {
+ name: 'Add Test Plans to the Test Queue'
+ });
+
+ fireEvent.click(manageAssistiveTechnologyVersionsButton);
+ fireEvent.click(addTestPlansToTestQueueButton);
+
+ expect(
+ queryByText(
+ 'Select an assistive technology and manage its versions in the ARIA-AT App'
+ )
+ ).toBeVisible();
+ expect(
+ queryByText(
+ 'Select a test plan, assistive technology and browser to add a new test plan report to the test queue.'
+ )
+ ).toBeVisible();
});
+ });
});
diff --git a/client/tests/TestRenderer/OutputTextArea.test.jsx b/client/tests/TestRenderer/OutputTextArea.test.jsx
index 4a466a714..20b9acabb 100644
--- a/client/tests/TestRenderer/OutputTextArea.test.jsx
+++ b/client/tests/TestRenderer/OutputTextArea.test.jsx
@@ -5,134 +5,134 @@
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import OutputTextArea from '../../components/TestRenderer/OutputTextArea';
-import '@testing-library/jest-dom/extend-expect';
+import '@testing-library/jest-dom';
import { NO_OUTPUT_STRING } from '../../components/TestRenderer/OutputTextArea/constants';
describe('OutputTextArea', () => {
- let atOutputMock;
- const change = value => {
- atOutputMock.value = value;
- };
+ let atOutputMock;
+ const change = value => {
+ atOutputMock.value = value;
+ };
- atOutputMock = {
- description: [
- 'Description',
- {
- required: true,
- highlightRequired: true,
- description: 'Highlight Description'
- }
- ],
- value: '',
- change: change,
- focus: false
- };
+ atOutputMock = {
+ description: [
+ 'Description',
+ {
+ required: true,
+ highlightRequired: true,
+ description: 'Highlight Description'
+ }
+ ],
+ value: '',
+ change: change,
+ focus: false
+ };
- it('should render with default settings', () => {
- render(
-
- );
- expect(screen.getByLabelText('Description')).toBeInTheDocument();
- expect(screen.getByLabelText('No output')).not.toBeChecked();
- });
+ it('should render with default settings', () => {
+ render(
+
+ );
+ expect(screen.getByLabelText('Description')).toBeInTheDocument();
+ expect(screen.getByLabelText('No output')).not.toBeChecked();
+ });
- it('should render correct label and checkbox', () => {
- render(
-
- );
- expect(screen.getByLabelText('Description')).toBeInTheDocument();
- expect(screen.getByLabelText('No output')).toBeInTheDocument();
- });
+ it('should render correct label and checkbox', () => {
+ render(
+
+ );
+ expect(screen.getByLabelText('Description')).toBeInTheDocument();
+ expect(screen.getByLabelText('No output')).toBeInTheDocument();
+ });
- it('should update the textarea with the correct value when checkbox is checked', () => {
- render(
-
- );
- const checkbox = screen.getByLabelText('No output');
- const textarea = screen.getByLabelText('Description');
+ it('should update the textarea with the correct value when checkbox is checked', () => {
+ render(
+
+ );
+ const checkbox = screen.getByLabelText('No output');
+ const textarea = screen.getByLabelText('Description');
- expect(textarea.value).toBe('');
+ expect(textarea.value).toBe('');
- fireEvent.click(checkbox);
- expect(atOutputMock.value).toBe(NO_OUTPUT_STRING);
- });
+ fireEvent.click(checkbox);
+ expect(atOutputMock.value).toBe(NO_OUTPUT_STRING);
+ });
- it('should handle textarea change', () => {
- render(
-
- );
- const textarea = screen.getByLabelText('Description');
- fireEvent.change(textarea, { target: { value: 'test value' } });
- expect(atOutputMock.value).toBe('test value');
- });
+ it('should handle textarea change', () => {
+ render(
+
+ );
+ const textarea = screen.getByLabelText('Description');
+ fireEvent.change(textarea, { target: { value: 'test value' } });
+ expect(atOutputMock.value).toBe('test value');
+ });
- it('should disable the textarea when the checkbox is checked', () => {
- render(
-
- );
- const checkbox = screen.getByLabelText('No output');
- const textarea = screen.getByLabelText('Description');
+ it('should disable the textarea when the checkbox is checked', () => {
+ render(
+
+ );
+ const checkbox = screen.getByLabelText('No output');
+ const textarea = screen.getByLabelText('Description');
- expect(textarea).not.toBeDisabled();
- fireEvent.click(checkbox);
- expect(textarea).toBeDisabled();
- });
+ expect(textarea).not.toBeDisabled();
+ fireEvent.click(checkbox);
+ expect(textarea).toBeDisabled();
+ });
- it('should have associated label for textarea', () => {
- render(
-
- );
- const textarea = screen.getByLabelText('Description');
- expect(textarea).toHaveAttribute('id', 'speechoutput-0');
- });
+ it('should have associated label for textarea', () => {
+ render(
+
+ );
+ const textarea = screen.getByLabelText('Description');
+ expect(textarea).toHaveAttribute('id', 'speechoutput-0');
+ });
- it('should disable checkbox when textarea has a value', () => {
- const prefilledMock = { ...atOutputMock, value: 'test value' };
- render(
-
- );
- const checkbox = screen.getByLabelText('No output');
- expect(checkbox).toBeDisabled();
- });
+ it('should disable checkbox when textarea has a value', () => {
+ const prefilledMock = { ...atOutputMock, value: 'test value' };
+ render(
+
+ );
+ const checkbox = screen.getByLabelText('No output');
+ expect(checkbox).toBeDisabled();
+ });
- it('should enable checkbox when textarea loads with no output default value', () => {
- const prefilledMock = { ...atOutputMock, value: NO_OUTPUT_STRING };
- render(
-
- );
- const checkbox = screen.getByLabelText('No output');
- expect(checkbox).not.toBeDisabled();
- });
+ it('should enable checkbox when textarea loads with no output default value', () => {
+ const prefilledMock = { ...atOutputMock, value: NO_OUTPUT_STRING };
+ render(
+
+ );
+ const checkbox = screen.getByLabelText('No output');
+ expect(checkbox).not.toBeDisabled();
+ });
});
diff --git a/client/tests/TestRun.test.jsx b/client/tests/TestRun.test.jsx
index b4359669a..d74b000d5 100644
--- a/client/tests/TestRun.test.jsx
+++ b/client/tests/TestRun.test.jsx
@@ -10,40 +10,40 @@ import { findByTestAttr } from './util';
import TestRun from '../components/TestRun';
const setup = () => {
- // Step into the higher order connected component and step into the contents of the UserSettings component
- const wrapper = shallow(
-
- )
- .dive()
- .dive();
- return wrapper;
+ // Step into the higher order connected component and step into the contents of the UserSettings component
+ const wrapper = shallow(
+
+ )
+ .dive()
+ .dive();
+ return wrapper;
};
describe.skip('render', () => {
- describe('loading when there are no tests', () => {
- let wrapper;
- beforeEach(() => {
- wrapper = setup();
- });
- test('renders not logged in text', () => {
- const component = findByTestAttr(wrapper, 'test-run-page-status');
- expect(component.text()).toContain('Loading');
- });
+ describe('loading when there are no tests', () => {
+ let wrapper;
+ beforeEach(() => {
+ wrapper = setup();
});
- describe('tests are loaded', () => {
- let wrapper;
- beforeEach(() => {
- wrapper = setup();
- wrapper.setState({ currentTestIndex: 1 });
- });
- test('renders testing headings', () => {
- let component = findByTestAttr(wrapper, 'apg-example-name');
- expect(component.length).toBe(1);
- expect(component.text()).toContain('Test Plan');
+ test('renders not logged in text', () => {
+ const component = findByTestAttr(wrapper, 'test-run-page-status');
+ expect(component.text()).toContain('Loading');
+ });
+ });
+ describe('tests are loaded', () => {
+ let wrapper;
+ beforeEach(() => {
+ wrapper = setup();
+ wrapper.setState({ currentTestIndex: 1 });
+ });
+ test('renders testing headings', () => {
+ let component = findByTestAttr(wrapper, 'apg-example-name');
+ expect(component.length).toBe(1);
+ expect(component.text()).toContain('Test Plan');
- component = findByTestAttr(wrapper, 'at-browser');
- expect(component.length).toBe(1);
- expect(component.text()).toContain('with');
- });
+ component = findByTestAttr(wrapper, 'at-browser');
+ expect(component.length).toBe(1);
+ expect(component.text()).toContain('with');
});
+ });
});
diff --git a/client/tests/UserSettings.test.jsx b/client/tests/UserSettings.test.jsx
index f639c453f..6eed0a264 100644
--- a/client/tests/UserSettings.test.jsx
+++ b/client/tests/UserSettings.test.jsx
@@ -6,50 +6,44 @@ import { findByTestAttr } from './util';
import UserSettings from '../components/UserSettings';
const setup = () => {
- // Step into the higher order connected component and step into the contents of the UserSettings component
- const wrapper = shallow( )
- .dive()
- .dive();
- return wrapper;
+ // Step into the higher order connected component and step into the contents of the UserSettings component
+ const wrapper = shallow( )
+ .dive()
+ .dive();
+ return wrapper;
};
describe.skip('render', () => {
- describe('user is not signed in', () => {
- let wrapper;
- beforeEach(() => {
- wrapper = setup();
- });
- test('renders component without error', () => {
- const component = findByTestAttr(wrapper, 'user-settings-contents');
- expect(component.length).toBe(1);
- });
- test('renders not signed in text', () => {
- const component = findByTestAttr(
- wrapper,
- 'user-settings-unauthorized'
- );
- expect(component.length).toBe(1);
- });
+ describe('user is not signed in', () => {
+ let wrapper;
+ beforeEach(() => {
+ wrapper = setup();
});
- describe('user is signed in', () => {
- let wrapper;
- beforeEach(() => {
- wrapper = setup();
- });
- test('renders component without error', () => {
- const component = findByTestAttr(wrapper, 'user-settings-contents');
- expect(component.length).toBe(1);
- });
- test('renders username', () => {
- const component = findByTestAttr(
- wrapper,
- 'user-settings-authorized'
- );
- expect(component.length).toBe(1);
- expect(component.text()).toContain('User Details');
- expect(component.find('a').at(0).props().href).toContain(
- 'https://github.com'
- );
- });
+ test('renders component without error', () => {
+ const component = findByTestAttr(wrapper, 'user-settings-contents');
+ expect(component.length).toBe(1);
});
+ test('renders not signed in text', () => {
+ const component = findByTestAttr(wrapper, 'user-settings-unauthorized');
+ expect(component.length).toBe(1);
+ });
+ });
+ describe('user is signed in', () => {
+ let wrapper;
+ beforeEach(() => {
+ wrapper = setup();
+ });
+ test('renders component without error', () => {
+ const component = findByTestAttr(wrapper, 'user-settings-contents');
+ expect(component.length).toBe(1);
+ });
+ test('renders username', () => {
+ const component = findByTestAttr(wrapper, 'user-settings-authorized');
+ expect(component.length).toBe(1);
+ expect(component.text()).toContain('User Details');
+ expect(component.find('a').at(0).props().href).toContain(
+ 'https://github.com'
+ );
+ });
+ });
});
diff --git a/client/tests/__mocks__/GraphQLMocks/DataManagementPagePopulatedMock.js b/client/tests/__mocks__/GraphQLMocks/DataManagementPagePopulatedMock.js
index 60973e8d7..7053cf593 100644
--- a/client/tests/__mocks__/GraphQLMocks/DataManagementPagePopulatedMock.js
+++ b/client/tests/__mocks__/GraphQLMocks/DataManagementPagePopulatedMock.js
@@ -1,7286 +1,7417 @@
export default (
- meQuery,
- dataManagementPageQuery,
- testPlanReportStatusDialogQuery
+ meQuery,
+ dataManagementPageQuery,
+ testPlanReportStatusDialogQuery
) => [
- {
- request: {
- query: meQuery
- },
- result: {
- data: {
- me: {
- id: '1',
- username: 'foo-bar',
- roles: ['ADMIN', 'TESTER']
- }
- }
+ {
+ request: {
+ query: meQuery
+ },
+ result: {
+ data: {
+ me: {
+ id: '1',
+ username: 'foo-bar',
+ roles: ['ADMIN', 'TESTER']
}
+ }
+ }
+ },
+ {
+ request: {
+ query: dataManagementPageQuery
},
- {
- request: {
- query: dataManagementPageQuery
+ result: {
+ data: {
+ me: {
+ id: '1',
+ username: 'foo-bar',
+ roles: ['ADMIN', 'TESTER']
},
- result: {
- data: {
- me: {
- id: '1',
- username: 'foo-bar',
- roles: ['ADMIN', 'TESTER']
- },
- ats: [
- {
- id: '1',
- key: 'jaws',
- name: 'JAWS',
- atVersions: [
- {
- id: '1',
- name: '2021.2111.13',
- releasedAt: '2021-11-01T04:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- }
- ],
- candidateBrowsers: [{ id: '2' }],
- recommendedBrowsers: [{ id: '1' }, { id: '2' }]
- },
- {
- id: '2',
- key: 'nvda',
- name: 'NVDA',
- atVersions: [
- {
- id: '2',
- name: '2020.4',
- releasedAt: '2021-02-19T05:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- }
- ],
- candidateBrowsers: [{ id: '2' }],
- recommendedBrowsers: [{ id: '1' }, { id: '2' }]
- },
+ ats: [
+ {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ releasedAt: '2021-11-01T04:00:00.000Z',
+ supportedByAutomation: false
+ }
+ ],
+ browsers: [
+ {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ }
+ ],
+ candidateBrowsers: [{ id: '2' }],
+ recommendedBrowsers: [{ id: '1' }, { id: '2' }]
+ },
+ {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ releasedAt: '2021-02-19T05:00:00.000Z',
+ supportedByAutomation: false
+ }
+ ],
+ browsers: [
+ {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ }
+ ],
+ candidateBrowsers: [{ id: '2' }],
+ recommendedBrowsers: [{ id: '1' }, { id: '2' }]
+ },
+ {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ releasedAt: '2019-09-01T04:00:00.000Z',
+ supportedByAutomation: false
+ }
+ ],
+ browsers: [
+ {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ }
+ ],
+ candidateBrowsers: [{ id: '3' }],
+ recommendedBrowsers: [{ id: '2' }, { id: '3' }]
+ }
+ ],
+ testPlans: [
+ {
+ id: '27',
+ directory: 'radiogroup-aria-activedescendant',
+ title: 'Radio Group Example Using aria-activedescendant'
+ },
+ {
+ id: '28',
+ directory: 'radiogroup-roving-tabindex',
+ title: 'Radio Group Example Using Roving tabindex'
+ },
+ {
+ id: '31',
+ directory: 'slider-multithumb',
+ title: 'Horizontal Multi-Thumb Slider'
+ },
+ {
+ id: '16',
+ directory: 'link-css',
+ title:
+ 'Link Example 3 (CSS :before content property on a span element)'
+ },
+ {
+ id: '17',
+ directory: 'link-img-alt',
+ title: 'Link Example 2 (img element with alt attribute)'
+ },
+ {
+ id: '1',
+ directory: 'alert',
+ title: 'Alert Example'
+ },
+ {
+ id: '13',
+ directory: 'disclosure-navigation',
+ title: 'Disclosure Navigation Menu Example'
+ },
+ {
+ id: '5',
+ directory: 'checkbox-tri-state',
+ title: 'Checkbox Example (Mixed-State)'
+ },
+ {
+ id: '3',
+ directory: 'breadcrumb',
+ title: 'Breadcrumb Example'
+ },
+ {
+ id: '19',
+ directory: 'main',
+ title: 'Main Landmark'
+ },
+ {
+ id: '24',
+ directory: 'meter',
+ title: 'Meter'
+ },
+ {
+ id: '32',
+ directory: 'switch',
+ title: 'Switch Example'
+ },
+ {
+ id: '26',
+ directory: 'modal-dialog',
+ title: 'Modal Dialog Example'
+ },
+ {
+ id: '22',
+ directory: 'menu-button-navigation',
+ title: 'Navigation Menu Button'
+ },
+ {
+ id: '34',
+ directory: 'toggle-button',
+ title: 'Toggle Button'
+ },
+ {
+ id: '18',
+ directory: 'link-span-text',
+ title: 'Link Example 1 (span element with text content)'
+ },
+ {
+ id: '8',
+ directory: 'command-button',
+ title: 'Command Button Example'
+ },
+ {
+ id: '15',
+ directory: 'horizontal-slider',
+ title: 'Color Viewer Slider'
+ },
+ {
+ id: '6',
+ directory: 'combobox-autocomplete-both-updated',
+ title: 'Combobox with Both List and Inline Autocomplete Example'
+ },
+ {
+ id: '7',
+ directory: 'combobox-select-only',
+ title: 'Select Only Combobox Example'
+ },
+ {
+ id: '4',
+ directory: 'checkbox',
+ title: 'Checkbox Example (Two State)'
+ },
+ {
+ id: '9',
+ directory: 'complementary',
+ title: 'Complementary Landmark'
+ },
+ {
+ id: '10',
+ directory: 'contentinfo',
+ title: 'Contentinfo Landmark'
+ },
+ {
+ id: '11',
+ directory: 'datepicker-spin-button',
+ title: 'Date Picker Spin Button Example'
+ },
+ {
+ id: '12',
+ directory: 'disclosure-faq',
+ title: 'Disclosure of Answers to Frequently Asked Questions Example'
+ },
+ {
+ id: '14',
+ directory: 'form',
+ title: 'Form Landmark'
+ },
+ {
+ id: '20',
+ directory: 'menu-button-actions',
+ title: 'Action Menu Button Example Using element.focus()'
+ },
+ {
+ id: '21',
+ directory: 'menu-button-actions-active-descendant',
+ title: 'Action Menu Button Example Using aria-activedescendant'
+ },
+ {
+ id: '23',
+ directory: 'menubar-editor',
+ title: 'Editor Menubar Example'
+ },
+ {
+ id: '25',
+ directory: 'minimal-data-grid',
+ title: 'Data Grid Example 1: Minimal Data Grid'
+ },
+ {
+ id: '29',
+ directory: 'rating-slider',
+ title: 'Rating Slider'
+ },
+ {
+ id: '30',
+ directory: 'seek-slider',
+ title: 'Media Seek Slider'
+ },
+ {
+ id: '33',
+ directory: 'tabs-manual-activation',
+ title: 'Tabs with Manual Activation'
+ },
+ {
+ id: '35',
+ directory: 'vertical-temperature-slider',
+ title: 'Vertical Temperature Slider'
+ },
+ {
+ id: '2',
+ directory: 'banner',
+ title: 'Banner Landmark'
+ }
+ ],
+ deprecatedTestPlanVersions: [],
+ testPlanVersions: [
+ {
+ 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',
+ versionString: 'V23.04.10',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'radiogroup-roving-tabindex'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ 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',
+ versionString: 'V23.04.10',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'radiogroup-aria-activedescendant'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '31',
+ title: 'Horizontal Multi-Thumb Slider',
+ phase: 'RD',
+ gitSha: 'b5fe3efd569518e449ef9a0978b0dec1f2a08bd6',
+ gitMessage:
+ 'Create tests for APG design pattern example: Horizontal Multi-Thumb Slider (#511)',
+ updatedAt: '2023-03-20T21:24:41.000Z',
+ versionString: 'V23.03.20',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'slider-multithumb'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '16',
+ title:
+ 'Link Example 3 (CSS :before content property on a span element)',
+ phase: 'RD',
+ gitSha: '7a8454bca6de980199868101431817cea03cce35',
+ gitMessage:
+ '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',
+ versionString: 'V23.03.13',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'link-css'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '17',
+ title: 'Link Example 2 (img element with alt attribute)',
+ phase: 'RD',
+ gitSha: 'dc637636cff74b51f5c468ff3b81bd1f38aefbb2',
+ gitMessage:
+ 'Create tests for APG design pattern example: Link Example 2 (img element with alt attribute) (#516)',
+ updatedAt: '2023-03-13T19:51:48.000Z',
+ versionString: 'V23.03.13',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'link-img-alt'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '1',
+ title: 'Alert Example',
+ phase: 'DRAFT',
+ gitSha: '0928bcf530efcf4faa677285439701537674e014',
+ gitMessage: 'Alert and Radiogroup/activedescendent updates (#865)',
+ updatedAt: '2022-12-08T21:47:42.000Z',
+ versionString: 'V22.12.08',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'alert'
+ },
+ testPlanReports: [
+ {
+ id: '7',
+ metrics: {},
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
{
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS',
- atVersions: [
- {
- id: '3',
- name: '11.6 (20G165)',
- releasedAt: '2019-09-01T04:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- }
- ],
- candidateBrowsers: [{ id: '3' }],
- recommendedBrowsers: [{ id: '2' }, { id: '3' }]
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
}
- ],
- testPlans: [
- {
- id: '27',
- directory: 'radiogroup-aria-activedescendant',
- title: 'Radio Group Example Using aria-activedescendant'
- },
- {
- id: '28',
- directory: 'radiogroup-roving-tabindex',
- title: 'Radio Group Example Using Roving tabindex'
- },
- {
- id: '31',
- directory: 'slider-multithumb',
- title: 'Horizontal Multi-Thumb Slider'
- },
- {
- id: '16',
- directory: 'link-css',
- title: 'Link Example 3 (CSS :before content property on a span element)'
- },
- {
- id: '17',
- directory: 'link-img-alt',
- title: 'Link Example 2 (img element with alt attribute)'
- },
- {
- id: '1',
- directory: 'alert',
- title: 'Alert Example'
- },
- {
- id: '13',
- directory: 'disclosure-navigation',
- title: 'Disclosure Navigation Menu Example'
- },
- {
- id: '5',
- directory: 'checkbox-tri-state',
- title: 'Checkbox Example (Mixed-State)'
- },
- {
- id: '3',
- directory: 'breadcrumb',
- title: 'Breadcrumb Example'
- },
- {
- id: '19',
- directory: 'main',
- title: 'Main Landmark'
- },
- {
- id: '24',
- directory: 'meter',
- title: 'Meter'
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ issues: [],
+ draftTestPlanRuns: []
+ }
+ ],
+ metadata: {}
+ },
+ {
+ id: '13',
+ title: 'Disclosure Navigation Menu Example',
+ phase: 'RD',
+ gitSha: '179ba0f438aaa5781b3ec8a4033d6bf9f757360b',
+ gitMessage:
+ 'Delete up arrow command for VoiceOver when navigating backwards to a disclosure button (#845)',
+ updatedAt: '2022-10-31T19:29:17.000Z',
+ versionString: 'V22.10.31',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'disclosure-navigation'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '3',
+ title: 'Breadcrumb Example',
+ phase: 'RD',
+ gitSha: '1aa3b74d24d340362e9f511eae33788d55487d12',
+ gitMessage:
+ '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',
+ versionString: 'V22.08.10',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'breadcrumb'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '19',
+ title: 'Main Landmark',
+ phase: 'RD',
+ gitSha: 'c87a66ea13a2b6fac6d79fe1fb0b7a2f721dcd22',
+ gitMessage:
+ 'Create updated tests for APG design pattern example: Main landmark (#707)',
+ updatedAt: '2022-08-05T17:46:37.000Z',
+ versionString: 'V22.08.05',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'main'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '24',
+ title: 'Meter',
+ phase: 'RD',
+ gitSha: '32d2d9db48becfc008fc566b569ac1563576ceb9',
+ gitMessage:
+ 'Create updated tests for APG design pattern example: Meter (#692)',
+ updatedAt: '2022-08-05T17:02:59.000Z',
+ versionString: 'V22.08.05',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'meter'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '32',
+ title: 'Switch Example',
+ phase: 'RD',
+ gitSha: '9d0e4e3d1040d64d9db69647e615c4ec0be723c2',
+ gitMessage:
+ 'Create updated tests for APG design pattern example: Switch (#691)',
+ updatedAt: '2022-08-05T16:13:44.000Z',
+ versionString: 'V22.08.05',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'switch'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '22',
+ title: 'Navigation Menu Button',
+ phase: 'RD',
+ gitSha: 'ecf05f484292189789f4db8b1ec41b19db38e567',
+ gitMessage:
+ 'Tasks 4, 5 and 6: corrected link name "Navigate backwards from here" (#734)',
+ updatedAt: '2022-05-26T16:14:17.000Z',
+ versionString: 'V22.05.26',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'menu-button-navigation'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '34',
+ title: 'Toggle Button',
+ phase: 'DRAFT',
+ gitSha: '022340081280b8cafb8ae0716a5b67e9ab942ef4',
+ gitMessage:
+ 'Delete duplicated assertion for operating a not pressed togle button (VoiceOver) (#716)',
+ updatedAt: '2022-05-18T20:51:40.000Z',
+ versionString: 'V22.05.18',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'toggle-button'
+ },
+ testPlanReports: [
+ {
+ id: '1',
+ metrics: {
+ testsCount: 16,
+ supportLevel: 'FAILING',
+ conflictsCount: 0,
+ supportPercent: 93,
+ testsFailedCount: 14,
+ testsPassedCount: 2,
+ shouldFormatted: false,
+ mustFormatted: '28 of 30 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 30,
+ unexpectedBehaviorCount: 1,
+ unexpectedBehaviorsFormatted: '1 found',
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 28
+ },
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '32',
- directory: 'switch',
- title: 'Switch Example'
+ testPlanReport: {
+ id: '1'
},
+ testResults: [
+ {
+ test: {
+ id: 'OWY5NeyIyIjoiMzQifQTRmOD'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:07.154Z'
+ },
+ {
+ test: {
+ id: 'NGFjMeyIyIjoiMzQifQjQxY2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'NTAwOeyIyIjoiMzQifQWI5YT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'YThjMeyIyIjoiMzQifQzIyYT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'YTgxMeyIyIjoiMzQifQzExOW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:07.381Z'
+ },
+ {
+ test: {
+ id: 'NGMwNeyIyIjoiMzQifQ2IwN2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:07.464Z'
+ },
+ {
+ test: {
+ id: 'YzQxNeyIyIjoiMzQifQjY5ND'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:07.537Z'
+ },
+ {
+ test: {
+ id: 'MjgwNeyIyIjoiMzQifQzk3YT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:07.610Z'
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ metadata: {}
+ },
+ {
+ id: '8',
+ title: 'Command Button Example',
+ phase: 'RD',
+ gitSha: '0c466eec96c8cafc9961232c85e14758c4589525',
+ gitMessage:
+ 'Fix navigation link positions in three test plans: link, command button and toggle button (#709)',
+ updatedAt: '2022-05-04T21:33:31.000Z',
+ versionString: 'V22.05.04',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'command-button'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '18',
+ title: 'Link Example 1 (span element with text content)',
+ phase: 'RD',
+ gitSha: '0c466eec96c8cafc9961232c85e14758c4589525',
+ gitMessage:
+ 'Fix navigation link positions in three test plans: link, command button and toggle button (#709)',
+ updatedAt: '2022-05-04T21:33:31.000Z',
+ versionString: 'V22.05.04',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'link-span-text'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '15',
+ title: 'Color Viewer Slider',
+ phase: 'RD',
+ gitSha: '1c6ef2fbef5fc056c622c802bebedaa14f2c8d40',
+ gitMessage:
+ 'Create updated tests for APG design pattern example: Color Viewer Slider (#686)',
+ updatedAt: '2022-04-14T18:06:40.000Z',
+ versionString: 'V22.04.14',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'horizontal-slider'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ 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',
+ versionString: 'V22.03.29',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'combobox-autocomplete-both-updated'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '21',
+ title: 'Action Menu Button Example Using aria-activedescendant',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'menu-button-actions-active-descendant'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '20',
+ title: 'Action Menu Button Example Using element.focus()',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'menu-button-actions'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '2',
+ title: 'Banner Landmark',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'banner'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '4',
+ title: 'Checkbox Example (Two State)',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'checkbox'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '9',
+ title: 'Complementary Landmark',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'complementary'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '10',
+ title: 'Contentinfo Landmark',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'contentinfo'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '25',
+ title: 'Data Grid Example 1: Minimal Data Grid',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'minimal-data-grid'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '11',
+ title: 'Date Picker Spin Button Example',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'datepicker-spin-button'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '12',
+ title:
+ 'Disclosure of Answers to Frequently Asked Questions Example',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'disclosure-faq'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '23',
+ title: 'Editor Menubar Example',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'menubar-editor'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '14',
+ title: 'Form Landmark',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'form'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '30',
+ title: 'Media Seek Slider',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'seek-slider'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '29',
+ title: 'Rating Slider',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'rating-slider'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '7',
+ title: 'Select Only Combobox Example',
+ phase: 'DRAFT',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'combobox-select-only'
+ },
+ testPlanReports: [
+ {
+ id: '2',
+ metrics: {
+ testsCount: 21,
+ supportLevel: 'FAILING',
+ conflictsCount: 5,
+ supportPercent: 96,
+ testsFailedCount: 16,
+ testsPassedCount: 5,
+ shouldFormatted: '3 of 3 passed',
+ mustFormatted: '48 of 50 passed',
+ shouldAssertionsCount: 3,
+ mustAssertionsCount: 50,
+ unexpectedBehaviorCount: 3,
+ unexpectedBehaviorsFormatted: '3 found',
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 3,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 48
+ },
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
{
- id: '26',
- directory: 'modal-dialog',
- title: 'Modal Dialog Example'
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'tom-proudfeet'
},
- {
- id: '22',
- directory: 'menu-button-navigation',
- title: 'Navigation Menu Button'
+ testPlanReport: {
+ id: '2'
},
- {
- id: '34',
- directory: 'toggle-button',
- title: 'Toggle Button'
+ testResults: [
+ {
+ test: {
+ id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.240Z'
+ },
+ {
+ test: {
+ id: 'MmY0YeyIyIjoiNyJ9jRkZD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.332Z'
+ },
+ {
+ test: {
+ id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.412Z'
+ },
+ {
+ test: {
+ id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.501Z'
+ },
+ {
+ test: {
+ id: 'MjRmNeyIyIjoiNyJ92MyMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.593Z'
+ },
+ {
+ test: {
+ id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.811Z'
+ },
+ {
+ test: {
+ id: 'YjZkYeyIyIjoiNyJ9WIxZm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.902Z'
+ },
+ {
+ test: {
+ id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.996Z'
+ }
+ ]
+ },
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '18',
- directory: 'link-span-text',
- title: 'Link Example 1 (span element with text content)'
+ testPlanReport: {
+ id: '2'
},
- {
- id: '8',
- directory: 'command-button',
- title: 'Command Button Example'
+ testResults: [
+ {
+ test: {
+ id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:07.718Z'
+ },
+ {
+ test: {
+ id: 'MmY0YeyIyIjoiNyJ9jRkZD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:07.813Z'
+ },
+ {
+ test: {
+ id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:07.914Z'
+ },
+ {
+ test: {
+ id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:07.988Z'
+ },
+ {
+ test: {
+ id: 'MjRmNeyIyIjoiNyJ92MyMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:08.074Z'
+ },
+ {
+ test: {
+ id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: null
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ metadata: {}
+ },
+ {
+ id: '33',
+ title: 'Tabs with Manual Activation',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'tabs-manual-activation'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ id: '35',
+ title: 'Vertical Temperature Slider',
+ phase: 'RD',
+ gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
+ gitMessage:
+ 'Generate html source script to support aria-at-app (#646)',
+ updatedAt: '2022-03-17T18:34:51.000Z',
+ versionString: 'V22.03.17',
+ draftPhaseReachedAt: null,
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'vertical-temperature-slider'
+ },
+ testPlanReports: [],
+ metadata: {}
+ },
+ {
+ 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',
+ versionString: 'V22.10.24',
+ 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'
+ },
+ testPlanReports: [
+ {
+ id: '6',
+ metrics: {
+ testsCount: 7,
+ supportLevel: 'FAILING',
+ conflictsCount: 0,
+ supportPercent: 96,
+ testsFailedCount: 3,
+ testsPassedCount: 4,
+ shouldFormatted: '4 of 4 passed',
+ mustFormatted: '44 of 46 passed',
+ shouldAssertionsCount: 4,
+ mustAssertionsCount: 46,
+ unexpectedBehaviorCount: 0,
+ unexpectedBehaviorsFormatted: false,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 4,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 44
+ },
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ isFinal: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS'
+ },
+ browser: {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'tom-proudfeet'
},
- {
- id: '15',
- directory: 'horizontal-slider',
- title: 'Color Viewer Slider'
+ testPlanReport: {
+ id: '6'
},
- {
- id: '6',
- directory: 'combobox-autocomplete-both-updated',
- title: 'Combobox with Both List and Inline Autocomplete Example'
+ testResults: [
+ {
+ test: {
+ id: 'YTE3NeyIyIjoiNSJ9WJlMj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.341Z'
+ },
+ {
+ test: {
+ id: 'YWJiOeyIyIjoiNSJ9GQ5Zm'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.405Z'
+ },
+ {
+ test: {
+ id: 'ZGFlYeyIyIjoiNSJ9TJlMW'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.474Z'
+ },
+ {
+ test: {
+ id: 'YjI2MeyIyIjoiNSJ9WE1OT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.537Z'
+ },
+ {
+ test: {
+ id: 'ZjAwZeyIyIjoiNSJ9TZmZj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.605Z'
+ },
+ {
+ test: {
+ id: 'MGRjZeyIyIjoiNSJ9WNiZD'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.670Z'
+ },
+ {
+ test: {
+ id: 'OTZmYeyIyIjoiNSJ9TU5Ym'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.739Z'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '12',
+ metrics: {
+ testsCount: 14,
+ supportLevel: 'FULL',
+ conflictsCount: 0,
+ supportPercent: 100,
+ testsFailedCount: 12,
+ testsPassedCount: 2,
+ shouldFormatted: false,
+ mustFormatted: '25 of 25 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 25,
+ unexpectedBehaviorCount: 0,
+ unexpectedBehaviorsFormatted: false,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 25
+ },
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '7',
- directory: 'combobox-select-only',
- title: 'Select Only Combobox Example'
+ testPlanReport: {
+ id: '12'
},
+ testResults: [
+ {
+ test: {
+ id: 'MTVlZeyIyIjoiNSJ9DUzMz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:11.764Z'
+ },
+ {
+ test: {
+ id: 'OThhMeyIyIjoiNSJ9WMxM2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:11.828Z'
+ },
+ {
+ test: {
+ id: 'YWNhNeyIyIjoiNSJ9TliN2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:11.892Z'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '13',
+ metrics: {
+ testsCount: 14,
+ supportLevel: 'FULL',
+ conflictsCount: 0,
+ supportPercent: 100,
+ testsFailedCount: 12,
+ testsPassedCount: 2,
+ shouldFormatted: false,
+ mustFormatted: '25 of 25 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 25,
+ unexpectedBehaviorCount: 0,
+ unexpectedBehaviorsFormatted: false,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 25
+ },
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
{
- id: '4',
- directory: 'checkbox',
- title: 'Checkbox Example (Two State)'
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '9',
- directory: 'complementary',
- title: 'Complementary Landmark'
+ testPlanReport: {
+ id: '13'
},
- {
- id: '10',
- directory: 'contentinfo',
- title: 'Contentinfo Landmark'
+ testResults: [
+ {
+ test: {
+ id: 'MTVlZeyIyIjoiNSJ9DUzMz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:11.955Z'
+ },
+ {
+ test: {
+ id: 'OThhMeyIyIjoiNSJ9WMxM2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:12.017Z'
+ },
+ {
+ test: {
+ id: 'YWNhNeyIyIjoiNSJ9TliN2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:12.083Z'
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ metadata: {}
+ },
+ {
+ 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',
+ versionString: 'V22.06.22',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ recommendedPhaseTargetDate: '2023-01-02T00:00:00.000Z',
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'modal-dialog'
+ },
+ testPlanReports: [
+ {
+ id: '10',
+ metrics: {
+ testsCount: 11,
+ supportLevel: 'FULL',
+ conflictsCount: 0,
+ supportPercent: 100,
+ testsFailedCount: 9,
+ testsPassedCount: 2,
+ shouldFormatted: false,
+ mustFormatted: '14 of 14 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 14,
+ unexpectedBehaviorCount: 0,
+ unexpectedBehaviorsFormatted: false,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 14
+ },
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS'
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '11',
- directory: 'datepicker-spin-button',
- title: 'Date Picker Spin Button Example'
+ testPlanReport: {
+ id: '10'
},
+ testResults: [
+ {
+ test: {
+ id: 'MzlmYeyIyIjoiMjYifQzIxY2'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:11.295Z'
+ },
+ {
+ test: {
+ id: 'N2FkZeyIyIjoiMjYifQDQ5NT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:11.369Z'
+ },
+ {
+ test: {
+ id: 'ZDJkYeyIyIjoiMjYifQzRkYj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:11.450Z'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '9',
+ metrics: {
+ testsCount: 18,
+ supportLevel: 'FULL',
+ conflictsCount: 0,
+ supportPercent: 100,
+ testsFailedCount: 16,
+ testsPassedCount: 2,
+ shouldFormatted: false,
+ mustFormatted: '16 of 16 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 16,
+ unexpectedBehaviorCount: 0,
+ unexpectedBehaviorsFormatted: false,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 16
+ },
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
{
- id: '12',
- directory: 'disclosure-faq',
- title: 'Disclosure of Answers to Frequently Asked Questions Example'
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '14',
- directory: 'form',
- title: 'Form Landmark'
+ testPlanReport: {
+ id: '9'
},
- {
- id: '20',
- directory: 'menu-button-actions',
- title: 'Action Menu Button Example Using element.focus()'
+ testResults: [
+ {
+ test: {
+ id: 'MThhNeyIyIjoiMjYifQmEyMj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:11.059Z'
+ },
+ {
+ test: {
+ id: 'ODY5MeyIyIjoiMjYifQzhmNW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:11.137Z'
+ },
+ {
+ test: {
+ id: 'NWVkNeyIyIjoiMjYifQTZkOT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:11.218Z'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '3',
+ metrics: {
+ testsCount: 18,
+ supportLevel: 'FAILING',
+ conflictsCount: 0,
+ supportPercent: 88,
+ testsFailedCount: 16,
+ testsPassedCount: 2,
+ shouldFormatted: false,
+ mustFormatted: '14 of 16 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 16,
+ unexpectedBehaviorCount: 1,
+ unexpectedBehaviorsFormatted: '1 found',
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 14
+ },
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ isFinal: true,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '21',
- directory: 'menu-button-actions-active-descendant',
- title: 'Action Menu Button Example Using aria-activedescendant'
+ testPlanReport: {
+ id: '3'
},
- {
- id: '23',
- directory: 'menubar-editor',
- title: 'Editor Menubar Example'
+ testResults: [
+ {
+ test: {
+ id: 'MThhNeyIyIjoiMjYifQmEyMj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.074Z'
+ },
+ {
+ test: {
+ id: 'NWVkNeyIyIjoiMjYifQTZkOT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.134Z'
+ },
+ {
+ test: {
+ id: 'NWM4NeyIyIjoiMjYifQDEwM2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.202Z'
+ },
+ {
+ test: {
+ id: 'NGFiZeyIyIjoiMjYifQWZiYW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.268Z'
+ },
+ {
+ test: {
+ id: 'MzQzYeyIyIjoiMjYifQzU5Zm'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.336Z'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '11',
+ metrics: {
+ testsCount: 11,
+ supportLevel: 'FULL',
+ conflictsCount: 0,
+ supportPercent: 100,
+ testsFailedCount: 9,
+ testsPassedCount: 2,
+ shouldFormatted: false,
+ mustFormatted: '14 of 14 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 14,
+ unexpectedBehaviorCount: 0,
+ unexpectedBehaviorsFormatted: false,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 14
+ },
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS'
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '25',
- directory: 'minimal-data-grid',
- title: 'Data Grid Example 1: Minimal Data Grid'
+ testPlanReport: {
+ id: '11'
},
+ testResults: [
+ {
+ test: {
+ id: 'MzlmYeyIyIjoiMjYifQzIxY2'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:11.532Z'
+ },
+ {
+ test: {
+ id: 'N2FkZeyIyIjoiMjYifQDQ5NT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:11.611Z'
+ },
+ {
+ test: {
+ id: 'ZDJkYeyIyIjoiMjYifQzRkYj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:11.696Z'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '4',
+ metrics: {
+ testsCount: 18,
+ supportLevel: 'FAILING',
+ conflictsCount: 0,
+ supportPercent: 91,
+ testsFailedCount: 14,
+ testsPassedCount: 4,
+ shouldFormatted: false,
+ mustFormatted: '20 of 22 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 22,
+ unexpectedBehaviorCount: 1,
+ unexpectedBehaviorsFormatted: '1 found',
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 20
+ },
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ isFinal: true,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
{
- id: '29',
- directory: 'rating-slider',
- title: 'Rating Slider'
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '30',
- directory: 'seek-slider',
- title: 'Media Seek Slider'
+ testPlanReport: {
+ id: '4'
},
- {
- id: '33',
- directory: 'tabs-manual-activation',
- title: 'Tabs with Manual Activation'
- },
- {
- id: '35',
- directory: 'vertical-temperature-slider',
- title: 'Vertical Temperature Slider'
- },
- {
- id: '2',
- directory: 'banner',
- title: 'Banner Landmark'
- }
- ],
- deprecatedTestPlanVersions: [],
- testPlanVersions: [
- {
- 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',
- versionString: 'V23.04.10',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'radiogroup-roving-tabindex'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- 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',
- versionString: 'V23.04.10',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'radiogroup-aria-activedescendant'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '31',
- title: 'Horizontal Multi-Thumb Slider',
- phase: 'RD',
- gitSha: 'b5fe3efd569518e449ef9a0978b0dec1f2a08bd6',
- gitMessage:
- 'Create tests for APG design pattern example: Horizontal Multi-Thumb Slider (#511)',
- updatedAt: '2023-03-20T21:24:41.000Z',
- versionString: 'V23.03.20',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'slider-multithumb'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '16',
- title: 'Link Example 3 (CSS :before content property on a span element)',
- phase: 'RD',
- gitSha: '7a8454bca6de980199868101431817cea03cce35',
- gitMessage:
- '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',
- versionString: 'V23.03.13',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'link-css'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '17',
- title: 'Link Example 2 (img element with alt attribute)',
- phase: 'RD',
- gitSha: 'dc637636cff74b51f5c468ff3b81bd1f38aefbb2',
- gitMessage:
- 'Create tests for APG design pattern example: Link Example 2 (img element with alt attribute) (#516)',
- updatedAt: '2023-03-13T19:51:48.000Z',
- versionString: 'V23.03.13',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'link-img-alt'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '1',
- title: 'Alert Example',
- phase: 'DRAFT',
- gitSha: '0928bcf530efcf4faa677285439701537674e014',
- gitMessage:
- 'Alert and Radiogroup/activedescendent updates (#865)',
- updatedAt: '2022-12-08T21:47:42.000Z',
- versionString: 'V22.12.08',
- draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'alert'
- },
- testPlanReports: [
- {
- id: '7',
- metrics: {},
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- issues: [],
- draftTestPlanRuns: []
- }
- ],
- metadata: {}
- },
- {
- id: '13',
- title: 'Disclosure Navigation Menu Example',
- phase: 'RD',
- gitSha: '179ba0f438aaa5781b3ec8a4033d6bf9f757360b',
- gitMessage:
- 'Delete up arrow command for VoiceOver when navigating backwards to a disclosure button (#845)',
- updatedAt: '2022-10-31T19:29:17.000Z',
- versionString: 'V22.10.31',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'disclosure-navigation'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '3',
- title: 'Breadcrumb Example',
- phase: 'RD',
- gitSha: '1aa3b74d24d340362e9f511eae33788d55487d12',
- gitMessage:
- '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',
- versionString: 'V22.08.10',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'breadcrumb'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '19',
- title: 'Main Landmark',
- phase: 'RD',
- gitSha: 'c87a66ea13a2b6fac6d79fe1fb0b7a2f721dcd22',
- gitMessage:
- 'Create updated tests for APG design pattern example: Main landmark (#707)',
- updatedAt: '2022-08-05T17:46:37.000Z',
- versionString: 'V22.08.05',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'main'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '24',
- title: 'Meter',
- phase: 'RD',
- gitSha: '32d2d9db48becfc008fc566b569ac1563576ceb9',
- gitMessage:
- 'Create updated tests for APG design pattern example: Meter (#692)',
- updatedAt: '2022-08-05T17:02:59.000Z',
- versionString: 'V22.08.05',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'meter'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '32',
- title: 'Switch Example',
- phase: 'RD',
- gitSha: '9d0e4e3d1040d64d9db69647e615c4ec0be723c2',
- gitMessage:
- 'Create updated tests for APG design pattern example: Switch (#691)',
- updatedAt: '2022-08-05T16:13:44.000Z',
- versionString: 'V22.08.05',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'switch'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '22',
- title: 'Navigation Menu Button',
- phase: 'RD',
- gitSha: 'ecf05f484292189789f4db8b1ec41b19db38e567',
- gitMessage:
- 'Tasks 4, 5 and 6: corrected link name "Navigate backwards from here" (#734)',
- updatedAt: '2022-05-26T16:14:17.000Z',
- versionString: 'V22.05.26',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'menu-button-navigation'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '34',
- title: 'Toggle Button',
- phase: 'DRAFT',
- gitSha: '022340081280b8cafb8ae0716a5b67e9ab942ef4',
- gitMessage:
- 'Delete duplicated assertion for operating a not pressed togle button (VoiceOver) (#716)',
- updatedAt: '2022-05-18T20:51:40.000Z',
- versionString: 'V22.05.18',
- draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'toggle-button'
- },
- testPlanReports: [
- {
- id: '1',
- metrics: {
- testsCount: 16,
- supportLevel: 'FAILING',
- conflictsCount: 0,
- supportPercent: 93,
- testsFailedCount: 14,
- testsPassedCount: 2,
- shouldFormatted: false,
- mustFormatted: '28 of 30 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 30,
- unexpectedBehaviorCount: 1,
- unexpectedBehaviorsFormatted: '1 found',
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 28
- },
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '1'
- },
- testResults: [
- {
- test: {
- id: 'OWY5NeyIyIjoiMzQifQTRmOD'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:07.154Z'
- },
- {
- test: {
- id: 'NGFjMeyIyIjoiMzQifQjQxY2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt: null
- },
- {
- test: {
- id: 'NTAwOeyIyIjoiMzQifQWI5YT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt: null
- },
- {
- test: {
- id: 'YThjMeyIyIjoiMzQifQzIyYT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt: null
- },
- {
- test: {
- id: 'YTgxMeyIyIjoiMzQifQzExOW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:07.381Z'
- },
- {
- test: {
- id: 'NGMwNeyIyIjoiMzQifQ2IwN2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:07.464Z'
- },
- {
- test: {
- id: 'YzQxNeyIyIjoiMzQifQjY5ND'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:07.537Z'
- },
- {
- test: {
- id: 'MjgwNeyIyIjoiMzQifQzk3YT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:07.610Z'
- }
- ]
- }
- ]
- }
- ],
- metadata: {}
- },
- {
- id: '8',
- title: 'Command Button Example',
- phase: 'RD',
- gitSha: '0c466eec96c8cafc9961232c85e14758c4589525',
- gitMessage:
- 'Fix navigation link positions in three test plans: link, command button and toggle button (#709)',
- updatedAt: '2022-05-04T21:33:31.000Z',
- versionString: 'V22.05.04',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'command-button'
- },
- testPlanReports: [],
- metadata: {}
- },
- {
- id: '18',
- title: 'Link Example 1 (span element with text content)',
- phase: 'RD',
- gitSha: '0c466eec96c8cafc9961232c85e14758c4589525',
- gitMessage:
- 'Fix navigation link positions in three test plans: link, command button and toggle button (#709)',
- updatedAt: '2022-05-04T21:33:31.000Z',
- versionString: 'V22.05.04',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'link-span-text'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'MThhNeyIyIjoiMjYifQmEyMj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.409Z'
+ },
+ {
+ test: {
+ id: 'NWVkNeyIyIjoiMjYifQTZkOT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.478Z'
+ },
+ {
+ test: {
+ id: 'NWM4NeyIyIjoiMjYifQDEwM2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.551Z'
+ },
+ {
+ test: {
+ id: 'NGFiZeyIyIjoiMjYifQWZiYW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.629Z'
+ },
+ {
+ test: {
+ id: 'MzQzYeyIyIjoiMjYifQzU5Zm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.704Z'
+ },
+ {
+ test: {
+ id: 'MmI1MeyIyIjoiMjYifQmU3Yz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.777Z'
+ },
+ {
+ test: {
+ id: 'YmRmYeyIyIjoiMjYifQjEyMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2023-08-18T03:17:09.852Z'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '5',
+ metrics: {
+ testsCount: 11,
+ supportLevel: 'FAILING',
+ conflictsCount: 0,
+ supportPercent: 92,
+ testsFailedCount: 8,
+ testsPassedCount: 3,
+ shouldFormatted: false,
+ mustFormatted: '23 of 25 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 25,
+ unexpectedBehaviorCount: 1,
+ unexpectedBehaviorsFormatted: '1 found',
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 23
+ },
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ isFinal: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS'
+ },
+ browser: {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '15',
- title: 'Color Viewer Slider',
- phase: 'RD',
- gitSha: '1c6ef2fbef5fc056c622c802bebedaa14f2c8d40',
- gitMessage:
- 'Create updated tests for APG design pattern example: Color Viewer Slider (#686)',
- updatedAt: '2022-04-14T18:06:40.000Z',
- versionString: 'V22.04.14',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'horizontal-slider'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '5'
},
- {
- 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',
- versionString: 'V22.03.29',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'combobox-autocomplete-both-updated'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'MzlmYeyIyIjoiMjYifQzIxY2'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:09.923Z'
+ },
+ {
+ test: {
+ id: 'ZDJkYeyIyIjoiMjYifQzRkYj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:09.991Z'
+ },
+ {
+ test: {
+ id: 'ZmQyNeyIyIjoiMjYifQ2M2ND'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.059Z'
+ },
+ {
+ test: {
+ id: 'OGE3YeyIyIjoiMjYifQjU1ND'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.129Z'
+ },
+ {
+ test: {
+ id: 'YWI3OeyIyIjoiMjYifQWJlNW'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.198Z'
+ },
+ {
+ test: {
+ id: 'M2RiOeyIyIjoiMjYifQGY1Nj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2023-08-18T03:17:10.272Z'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '8',
+ metrics: {
+ testsCount: 18,
+ supportLevel: 'FULL',
+ conflictsCount: 0,
+ supportPercent: 100,
+ testsFailedCount: 16,
+ testsPassedCount: 2,
+ shouldFormatted: false,
+ mustFormatted: '16 of 16 passed',
+ shouldAssertionsCount: 0,
+ mustAssertionsCount: 16,
+ unexpectedBehaviorCount: 0,
+ unexpectedBehaviorsFormatted: false,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 16
+ },
+ markedFinalAt: null,
+ isFinal: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '21',
- title: 'Action Menu Button Example Using aria-activedescendant',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'menu-button-actions-active-descendant'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '8'
},
- {
- id: '20',
- title: 'Action Menu Button Example Using element.focus()',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'menu-button-actions'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'MThhNeyIyIjoiMjYifQmEyMj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:10.817Z'
+ },
+ {
+ test: {
+ id: 'ODY5MeyIyIjoiMjYifQzhmNW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:10.894Z'
+ },
+ {
+ test: {
+ id: 'NWVkNeyIyIjoiMjYifQTZkOT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2023-08-18T03:17:10.979Z'
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ metadata: {}
+ }
+ ]
+ }
+ }
+ },
+ {
+ request: {
+ query: testPlanReportStatusDialogQuery,
+ variables: { testPlanVersionId: '1' }
+ },
+ result: {
+ data: {
+ testPlanVersion: {
+ id: '1',
+ title: 'Alert Example',
+ phase: 'DRAFT',
+ gitSha: 'c665367f3742c2b607f7b3c2655782188b93f302',
+ gitMessage:
+ 'Create updated tests for APG design pattern example: Alert (#685)',
+ updatedAt: '2022-04-14T17:59:42.000Z',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'alert'
+ },
+ testPlanReportStatuses: [
+ {
+ isRequired: true,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '101',
+ metrics: {},
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [],
+ draftTestPlanRuns: []
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'chrome',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '7',
+ metrics: {},
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [],
+ draftTestPlanRuns: []
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ request: {
+ query: testPlanReportStatusDialogQuery,
+ variables: { testPlanVersionId: '5' }
+ },
+ result: {
+ data: {
+ testPlanVersion: {
+ id: '69',
+ title: 'Checkbox Example (Mixed-State)',
+ phase: 'RECOMMENDED',
+ gitSha: '836fb2a997f5b2844035b8c934f8fda9833cd5b2',
+ gitMessage: 'Validation for test csv formats (#980)',
+ updatedAt: '2023-08-23T20:30:34.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'
+ },
+ testPlanReportStatuses: [
+ {
+ isRequired: true,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '12',
+ metrics: {
+ testsCount: 14,
+ mayFormatted: false,
+ supportLevel: 'FULL',
+ commandsCount: 26,
+ mustFormatted: '116 of 116 passed',
+ conflictsCount: 0,
+ supportPercent: 100,
+ shouldFormatted: '34 of 34 passed',
+ testsFailedCount: 0,
+ testsPassedCount: 14,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 116,
+ assertionsFailedCount: 0,
+ assertionsPassedCount: 150,
+ shouldAssertionsCount: 34,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 116,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 34,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 26,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 26
+ },
+ isFinal: true,
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '2',
- title: 'Banner Landmark',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'banner'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '12'
},
- {
- id: '4',
- title: 'Checkbox Example (Two State)',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'checkbox'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'YWYzOeyIyIjoiNjkifQTQ0MT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.053Z'
+ },
+ {
+ test: {
+ id: 'OGZjNeyIyIjoiNjkifQjQxZW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.149Z'
+ },
+ {
+ test: {
+ id: 'NjM3ZeyIyIjoiNjkifQmUxYz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.242Z'
+ },
+ {
+ test: {
+ id: 'ZWQ0MeyIyIjoiNjkifQGZhYT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.321Z'
+ },
+ {
+ test: {
+ id: 'ZGI3ZeyIyIjoiNjkifQTc5Mj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.409Z'
+ },
+ {
+ test: {
+ id: 'MDZjOeyIyIjoiNjkifQGJkYz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.510Z'
+ },
+ {
+ test: {
+ id: 'ZmI3NeyIyIjoiNjkifQzUwMT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.614Z'
+ },
+ {
+ test: {
+ id: 'NmY2YeyIyIjoiNjkifQTczOW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.731Z'
+ },
+ {
+ test: {
+ id: 'MjIwYeyIyIjoiNjkifQmUzZj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:52.882Z'
+ },
+ {
+ test: {
+ id: 'ODg0OeyIyIjoiNjkifQWFlYm'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:53.038Z'
+ },
+ {
+ test: {
+ id: 'ZDQ2MeyIyIjoiNjkifQjlmZj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:53.184Z'
+ },
+ {
+ test: {
+ id: 'MjdlYeyIyIjoiNjkifQTgyNj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:53.317Z'
+ },
+ {
+ test: {
+ id: 'OGE5MeyIyIjoiNjkifQGZjOT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:53.428Z'
+ },
+ {
+ test: {
+ id: 'YWNlNeyIyIjoiNjkifQjQzOW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:53.567Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '13',
+ metrics: {
+ testsCount: 14,
+ mayFormatted: false,
+ supportLevel: 'FULL',
+ commandsCount: 28,
+ mustFormatted: '124 of 124 passed',
+ conflictsCount: 0,
+ supportPercent: 100,
+ shouldFormatted: '36 of 36 passed',
+ testsFailedCount: 0,
+ testsPassedCount: 14,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 124,
+ assertionsFailedCount: 0,
+ assertionsPassedCount: 160,
+ shouldAssertionsCount: 36,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 124,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 36,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 28,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 28
+ },
+ isFinal: true,
+ markedFinalAt: '2022-07-07T00:00:00.000Z',
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '9',
- title: 'Complementary Landmark',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'complementary'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '13'
},
- {
- id: '10',
- title: 'Contentinfo Landmark',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'contentinfo'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'YWYzOeyIyIjoiNjkifQTQ0MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:53.698Z'
+ },
+ {
+ test: {
+ id: 'OGZjNeyIyIjoiNjkifQjQxZW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:53.814Z'
+ },
+ {
+ test: {
+ id: 'NjM3ZeyIyIjoiNjkifQmUxYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:53.921Z'
+ },
+ {
+ test: {
+ id: 'ZWQ0MeyIyIjoiNjkifQGZhYT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.038Z'
+ },
+ {
+ test: {
+ id: 'ZGI3ZeyIyIjoiNjkifQTc5Mj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.181Z'
+ },
+ {
+ test: {
+ id: 'MDZjOeyIyIjoiNjkifQGJkYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.327Z'
+ },
+ {
+ test: {
+ id: 'ZmI3NeyIyIjoiNjkifQzUwMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.421Z'
+ },
+ {
+ test: {
+ id: 'NmY2YeyIyIjoiNjkifQTczOW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.541Z'
+ },
+ {
+ test: {
+ id: 'MjIwYeyIyIjoiNjkifQmUzZj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.666Z'
+ },
+ {
+ test: {
+ id: 'ODg0OeyIyIjoiNjkifQWFlYm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.794Z'
+ },
+ {
+ test: {
+ id: 'ZDQ2MeyIyIjoiNjkifQjlmZj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.881Z'
+ },
+ {
+ test: {
+ id: 'MjdlYeyIyIjoiNjkifQTgyNj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:54.967Z'
+ },
+ {
+ test: {
+ id: 'OGE5MeyIyIjoiNjkifQGZjOT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.059Z'
+ },
+ {
+ test: {
+ id: 'YWNlNeyIyIjoiNjkifQjQzOW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.140Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '6',
+ metrics: {
+ testsCount: 7,
+ mayFormatted: false,
+ supportLevel: 'FAILING',
+ commandsCount: 16,
+ mustFormatted: '66 of 68 passed',
+ conflictsCount: 0,
+ supportPercent: 97,
+ shouldFormatted: '20 of 20 passed',
+ testsFailedCount: 2,
+ testsPassedCount: 5,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 68,
+ assertionsFailedCount: 2,
+ assertionsPassedCount: 86,
+ shouldAssertionsCount: 20,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 66,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 20,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 16,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 16
+ },
+ isFinal: true,
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'tom-proudfeet'
},
- {
- id: '25',
- title: 'Data Grid Example 1: Minimal Data Grid',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'minimal-data-grid'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '6'
},
- {
- id: '11',
- title: 'Date Picker Spin Button Example',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'datepicker-spin-button'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'NmUzMeyIyIjoiNjkifQmU0OT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.554Z'
+ },
+ {
+ test: {
+ id: 'Y2UyYeyIyIjoiNjkifQ2Y1Mz'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.671Z'
+ },
+ {
+ test: {
+ id: 'ODc2OeyIyIjoiNjkifQTA1Yz'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.794Z'
+ },
+ {
+ test: {
+ id: 'OTgwZeyIyIjoiNjkifQDZjOG'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.909Z'
+ },
+ {
+ test: {
+ id: 'ODA3ZeyIyIjoiNjkifQjI4Y2'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.997Z'
+ },
+ {
+ test: {
+ id: 'OWI4MeyIyIjoiNjkifQzFlZD'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:46.091Z'
+ },
+ {
+ test: {
+ id: 'MzhiZeyIyIjoiNjkifQWE4Nj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:46.189Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ request: {
+ query: testPlanReportStatusDialogQuery,
+ variables: { testPlanVersionId: '7' }
+ },
+ result: {
+ data: {
+ testPlanVersion: {
+ id: '7',
+ title: 'Select Only Combobox Example',
+ phase: 'DRAFT',
+ 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: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'combobox-select-only'
+ },
+ testPlanReportStatuses: [
+ {
+ isRequired: true,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '2',
+ metrics: {
+ testsCount: 21,
+ mayFormatted: false,
+ supportLevel: 'FAILING',
+ commandsCount: 24,
+ mustFormatted: '118 of 122 passed',
+ conflictsCount: 3,
+ supportPercent: 97,
+ shouldFormatted: '34 of 36 passed',
+ testsFailedCount: 6,
+ testsPassedCount: 15,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 122,
+ assertionsFailedCount: 6,
+ assertionsPassedCount: 152,
+ shouldAssertionsCount: 36,
+ unexpectedBehaviorCount: 3,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 4,
+ mustAssertionsPassedCount: 118,
+ shouldAssertionsFailedCount: 2,
+ shouldAssertionsPassedCount: 34,
+ unexpectedBehaviorsFormatted: '3 found',
+ severeImpactFailedAssertionCount: 1,
+ severeImpactPassedAssertionCount: 23,
+ moderateImpactFailedAssertionCount: 2,
+ moderateImpactPassedAssertionCount: 22
+ },
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [
+ {
+ link: 'https://github.com/bocoup/aria-at/issues/128#issue-2157878584',
+ isOpen: true,
+ feedbackType: 'FEEDBACK'
+ }
+ ],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'tom-proudfeet'
},
- {
- id: '12',
- title: 'Disclosure of Answers to Frequently Asked Questions Example',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'disclosure-faq'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '2'
},
- {
- id: '23',
- title: 'Editor Menubar Example',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'menubar-editor'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.949Z'
+ },
+ {
+ test: {
+ id: 'MmY0YeyIyIjoiNyJ9jRkZD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.070Z'
+ },
+ {
+ test: {
+ id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.180Z'
+ },
+ {
+ test: {
+ id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.292Z'
+ },
+ {
+ test: {
+ id: 'MjRmNeyIyIjoiNyJ92MyMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.406Z'
+ },
+ {
+ test: {
+ id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.640Z'
+ },
+ {
+ test: {
+ id: 'YjZkYeyIyIjoiNyJ9WIxZm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.761Z'
+ },
+ {
+ test: {
+ id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.888Z'
+ },
+ {
+ test: {
+ id: 'MmZkNeyIyIjoiNyJ9zIwN2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.006Z'
+ },
+ {
+ test: {
+ id: 'ZmQwOeyIyIjoiNyJ9DEzYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.122Z'
+ },
+ {
+ test: {
+ id: 'MGViNeyIyIjoiNyJ9GQ3MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.234Z'
+ },
+ {
+ test: {
+ id: 'YTg5MeyIyIjoiNyJ9WEzOT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.355Z'
+ },
+ {
+ test: {
+ id: 'NTRjMeyIyIjoiNyJ9zQ0OD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.467Z'
+ },
+ {
+ test: {
+ id: 'MjRlZeyIyIjoiNyJ9DcyY2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.588Z'
+ },
+ {
+ test: {
+ id: 'YWQzNeyIyIjoiNyJ9mE2Nm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.712Z'
+ },
+ {
+ test: {
+ id: 'OTYxOeyIyIjoiNyJ9TdmYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.826Z'
+ },
+ {
+ test: {
+ id: 'MjgzNeyIyIjoiNyJ9TZjNz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.948Z'
+ },
+ {
+ test: {
+ id: 'NWNiZeyIyIjoiNyJ9jI2MD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:41.075Z'
+ }
+ ]
+ },
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '14',
- title: 'Form Landmark',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'form'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '2'
},
- {
- id: '30',
- title: 'Media Seek Slider',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'seek-slider'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:36.666Z'
+ },
+ {
+ test: {
+ id: 'MmY0YeyIyIjoiNyJ9jRkZD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:36.793Z'
+ },
+ {
+ test: {
+ id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:36.914Z'
+ },
+ {
+ test: {
+ id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.031Z'
+ },
+ {
+ test: {
+ id: 'MjRmNeyIyIjoiNyJ92MyMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.150Z'
+ },
+ {
+ test: {
+ id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.384Z'
+ },
+ {
+ test: {
+ id: 'YjZkYeyIyIjoiNyJ9WIxZm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.512Z'
+ },
+ {
+ test: {
+ id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.638Z'
+ },
+ {
+ test: {
+ id: 'MmZkNeyIyIjoiNyJ9zIwN2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.760Z'
+ },
+ {
+ test: {
+ id: 'ZmQwOeyIyIjoiNyJ9DEzYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.883Z'
+ },
+ {
+ test: {
+ id: 'MGViNeyIyIjoiNyJ9GQ3MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.014Z'
+ },
+ {
+ test: {
+ id: 'YTg5MeyIyIjoiNyJ9WEzOT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.145Z'
+ },
+ {
+ test: {
+ id: 'NTRjMeyIyIjoiNyJ9zQ0OD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.268Z'
+ },
+ {
+ test: {
+ id: 'MjRlZeyIyIjoiNyJ9DcyY2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.382Z'
+ },
+ {
+ test: {
+ id: 'YWQzNeyIyIjoiNyJ9mE2Nm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.481Z'
+ },
+ {
+ test: {
+ id: 'OTYxOeyIyIjoiNyJ9TdmYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.596Z'
+ },
+ {
+ test: {
+ id: 'MjgzNeyIyIjoiNyJ9TZjNz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.701Z'
+ },
+ {
+ test: {
+ id: 'NWNiZeyIyIjoiNyJ9jI2MD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.811Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ request: {
+ query: testPlanReportStatusDialogQuery,
+ variables: { testPlanVersionId: '26' }
+ },
+ result: {
+ data: {
+ testPlanVersion: {
+ id: '24',
+ title: 'Modal Dialog Example',
+ phase: 'CANDIDATE',
+ gitSha: '5fe7afd82fe51c185b8661276105190a59d47322',
+ gitMessage: 'Task 7: delete incorrect instructions (#733)',
+ updatedAt: '2022-05-26T16:10:10.000Z',
+ draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ candidatePhaseReachedAt: '2022-07-06T00:00:00.000Z',
+ recommendedPhaseTargetDate: '2023-01-02T00:00:00.000Z',
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'modal-dialog'
+ },
+ testPlanReportStatuses: [
+ {
+ isRequired: true,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '3',
+ metrics: {
+ testsCount: 18,
+ mayFormatted: false,
+ supportLevel: 'FAILING',
+ commandsCount: 26,
+ mustFormatted: '115 of 117 passed',
+ conflictsCount: 0,
+ supportPercent: 98,
+ shouldFormatted: '25 of 26 passed',
+ testsFailedCount: 2,
+ testsPassedCount: 16,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 117,
+ assertionsFailedCount: 3,
+ assertionsPassedCount: 140,
+ shouldAssertionsCount: 26,
+ unexpectedBehaviorCount: 1,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 115,
+ shouldAssertionsFailedCount: 1,
+ shouldAssertionsPassedCount: 25,
+ unexpectedBehaviorsFormatted: '1 found',
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 26,
+ moderateImpactFailedAssertionCount: 1,
+ moderateImpactPassedAssertionCount: 25
+ },
+ isFinal: true,
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '29',
- title: 'Rating Slider',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'rating-slider'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '3'
},
- {
- id: '7',
- title: 'Select Only Combobox Example',
- phase: 'DRAFT',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'combobox-select-only'
- },
- testPlanReports: [
- {
- id: '2',
- metrics: {
- testsCount: 21,
- supportLevel: 'FAILING',
- conflictsCount: 5,
- supportPercent: 96,
- testsFailedCount: 16,
- testsPassedCount: 5,
- shouldFormatted: '3 of 3 passed',
- mustFormatted: '48 of 50 passed',
- shouldAssertionsCount: 3,
- mustAssertionsCount: 50,
- unexpectedBehaviorCount: 3,
- unexpectedBehaviorsFormatted: '3 found',
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 3,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 48
- },
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'tom-proudfeet'
- },
- testPlanReport: {
- id: '2'
- },
- testResults: [
- {
- test: {
- id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.240Z'
- },
- {
- test: {
- id: 'MmY0YeyIyIjoiNyJ9jRkZD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.332Z'
- },
- {
- test: {
- id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.412Z'
- },
- {
- test: {
- id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.501Z'
- },
- {
- test: {
- id: 'MjRmNeyIyIjoiNyJ92MyMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.593Z'
- },
- {
- test: {
- id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: null
- },
- {
- test: {
- id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.811Z'
- },
- {
- test: {
- id: 'YjZkYeyIyIjoiNyJ9WIxZm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.902Z'
- },
- {
- test: {
- id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.996Z'
- }
- ]
- },
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '2'
- },
- testResults: [
- {
- test: {
- id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:07.718Z'
- },
- {
- test: {
- id: 'MmY0YeyIyIjoiNyJ9jRkZD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:07.813Z'
- },
- {
- test: {
- id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:07.914Z'
- },
- {
- test: {
- id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:07.988Z'
- },
- {
- test: {
- id: 'MjRmNeyIyIjoiNyJ92MyMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:08.074Z'
- },
- {
- test: {
- id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: null
- }
- ]
- }
- ]
- }
- ],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'ZjE0NeyIyIjoiMjQifQmI0NT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.177Z'
+ },
+ {
+ test: {
+ id: 'YjZlNeyIyIjoiMjQifQTc5ZW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.256Z'
+ },
+ {
+ test: {
+ id: 'NWM0MeyIyIjoiMjQifQzhiYz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.349Z'
+ },
+ {
+ test: {
+ id: 'YzM0ZeyIyIjoiMjQifQmRmMz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.419Z'
+ },
+ {
+ test: {
+ id: 'ZjVjMeyIyIjoiMjQifQDRhY2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.496Z'
+ },
+ {
+ test: {
+ id: 'YmUzMeyIyIjoiMjQifQmRmNm'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.574Z'
+ },
+ {
+ test: {
+ id: 'ZGJmMeyIyIjoiMjQifQzU5Yz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.661Z'
+ },
+ {
+ test: {
+ id: 'Nzg1OeyIyIjoiMjQifQTYxM2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.741Z'
+ },
+ {
+ test: {
+ id: 'ZTI0MeyIyIjoiMjQifQzM4YT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.823Z'
+ },
+ {
+ test: {
+ id: 'MzY5ZeyIyIjoiMjQifQmQ0OT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:41.950Z'
+ },
+ {
+ test: {
+ id: 'ZjVmYeyIyIjoiMjQifQjJjYW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.044Z'
+ },
+ {
+ test: {
+ id: 'ZTMwNeyIyIjoiMjQifQzI5Nz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.112Z'
+ },
+ {
+ test: {
+ id: 'NGY0MeyIyIjoiMjQifQ2FjMj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.191Z'
+ },
+ {
+ test: {
+ id: 'OTI0OeyIyIjoiMjQifQTU1ZT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.271Z'
+ },
+ {
+ test: {
+ id: 'MDRhMeyIyIjoiMjQifQWEzMj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.344Z'
+ },
+ {
+ test: {
+ id: 'ZThlZeyIyIjoiMjQifQjQ2Nz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.412Z'
+ },
+ {
+ test: {
+ id: 'NjhjMeyIyIjoiMjQifQGE0ND'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.482Z'
+ },
+ {
+ test: {
+ id: 'YTAzZeyIyIjoiMjQifQTc5ZD'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.575Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: null,
+ exactAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ testPlanReport: {
+ id: '105',
+ metrics: {},
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [],
+ draftTestPlanRuns: []
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '8',
+ metrics: {
+ testsCount: 18,
+ mayFormatted: false,
+ supportLevel: 'FULL',
+ commandsCount: 26,
+ mustFormatted: '117 of 117 passed',
+ conflictsCount: 0,
+ supportPercent: 100,
+ shouldFormatted: '26 of 26 passed',
+ testsFailedCount: 0,
+ testsPassedCount: 18,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 117,
+ assertionsFailedCount: 0,
+ assertionsPassedCount: 143,
+ shouldAssertionsCount: 26,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 117,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 26,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 26,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 26
+ },
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
+ },
+ testPlanReport: {
+ id: '8'
+ },
+ testResults: [
+ {
+ test: {
+ id: 'ZjE0NeyIyIjoiMjQifQmI0NT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.300Z'
+ },
+ {
+ test: {
+ id: 'YjZlNeyIyIjoiMjQifQTc5ZW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.396Z'
+ },
+ {
+ test: {
+ id: 'NWM0MeyIyIjoiMjQifQzhiYz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.490Z'
+ },
+ {
+ test: {
+ id: 'YzM0ZeyIyIjoiMjQifQmRmMz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.572Z'
+ },
+ {
+ test: {
+ id: 'ZjVjMeyIyIjoiMjQifQDRhY2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.656Z'
+ },
+ {
+ test: {
+ id: 'YmUzMeyIyIjoiMjQifQmRmNm'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.743Z'
+ },
+ {
+ test: {
+ id: 'ZGJmMeyIyIjoiMjQifQzU5Yz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.828Z'
+ },
+ {
+ test: {
+ id: 'Nzg1OeyIyIjoiMjQifQTYxM2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.912Z'
+ },
+ {
+ test: {
+ id: 'ZTI0MeyIyIjoiMjQifQzM4YT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:46.990Z'
+ },
+ {
+ test: {
+ id: 'MzY5ZeyIyIjoiMjQifQmQ0OT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:47.087Z'
+ },
+ {
+ test: {
+ id: 'ZjVmYeyIyIjoiMjQifQjJjYW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:47.215Z'
+ },
+ {
+ test: {
+ id: 'ZTMwNeyIyIjoiMjQifQzI5Nz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:47.355Z'
+ },
+ {
+ test: {
+ id: 'NGY0MeyIyIjoiMjQifQ2FjMj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:47.484Z'
+ },
+ {
+ test: {
+ id: 'OTI0OeyIyIjoiMjQifQTU1ZT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:47.614Z'
+ },
+ {
+ test: {
+ id: 'MDRhMeyIyIjoiMjQifQWEzMj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:47.738Z'
+ },
+ {
+ test: {
+ id: 'ZThlZeyIyIjoiMjQifQjQ2Nz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:47.862Z'
+ },
+ {
+ test: {
+ id: 'NjhjMeyIyIjoiMjQifQGE0ND'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:47.981Z'
+ },
+ {
+ test: {
+ id: 'YTAzZeyIyIjoiMjQifQTc5ZD'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.105Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '4',
+ metrics: {
+ testsCount: 18,
+ mayFormatted: false,
+ supportLevel: 'FAILING',
+ commandsCount: 26,
+ mustFormatted: '115 of 117 passed',
+ conflictsCount: 0,
+ supportPercent: 98,
+ shouldFormatted: '25 of 26 passed',
+ testsFailedCount: 2,
+ testsPassedCount: 16,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 117,
+ assertionsFailedCount: 3,
+ assertionsPassedCount: 140,
+ shouldAssertionsCount: 26,
+ unexpectedBehaviorCount: 1,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 115,
+ shouldAssertionsFailedCount: 1,
+ shouldAssertionsPassedCount: 25,
+ unexpectedBehaviorsFormatted: '1 found',
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 26,
+ moderateImpactFailedAssertionCount: 1,
+ moderateImpactPassedAssertionCount: 25
+ },
+ isFinal: true,
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
+ },
+ testPlanReport: {
+ id: '4'
+ },
+ testResults: [
+ {
+ test: {
+ id: 'ZjE0NeyIyIjoiMjQifQmI0NT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.683Z'
+ },
+ {
+ test: {
+ id: 'YjZlNeyIyIjoiMjQifQTc5ZW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.772Z'
+ },
+ {
+ test: {
+ id: 'NWM0MeyIyIjoiMjQifQzhiYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.848Z'
+ },
+ {
+ test: {
+ id: 'YzM0ZeyIyIjoiMjQifQmRmMz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:42.928Z'
+ },
+ {
+ test: {
+ id: 'ZjVjMeyIyIjoiMjQifQDRhY2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.005Z'
+ },
+ {
+ test: {
+ id: 'YmUzMeyIyIjoiMjQifQmRmNm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.093Z'
+ },
+ {
+ test: {
+ id: 'ZGJmMeyIyIjoiMjQifQzU5Yz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.173Z'
+ },
+ {
+ test: {
+ id: 'Nzg1OeyIyIjoiMjQifQTYxM2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.245Z'
+ },
+ {
+ test: {
+ id: 'ZTI0MeyIyIjoiMjQifQzM4YT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.318Z'
+ },
+ {
+ test: {
+ id: 'MzY5ZeyIyIjoiMjQifQmQ0OT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.399Z'
+ },
+ {
+ test: {
+ id: 'ZjVmYeyIyIjoiMjQifQjJjYW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.473Z'
+ },
+ {
+ test: {
+ id: 'ZTMwNeyIyIjoiMjQifQzI5Nz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.554Z'
+ },
+ {
+ test: {
+ id: 'NGY0MeyIyIjoiMjQifQ2FjMj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.643Z'
+ },
+ {
+ test: {
+ id: 'OTI0OeyIyIjoiMjQifQTU1ZT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.738Z'
+ },
+ {
+ test: {
+ id: 'MDRhMeyIyIjoiMjQifQWEzMj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.844Z'
+ },
+ {
+ test: {
+ id: 'ZThlZeyIyIjoiMjQifQjQ2Nz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:43.950Z'
+ },
+ {
+ test: {
+ id: 'NjhjMeyIyIjoiMjQifQGE0ND'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:44.078Z'
+ },
+ {
+ test: {
+ id: 'YTAzZeyIyIjoiMjQifQTc5ZD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:44.219Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '9',
+ metrics: {
+ testsCount: 18,
+ mayFormatted: false,
+ supportLevel: 'FULL',
+ commandsCount: 26,
+ mustFormatted: '117 of 117 passed',
+ conflictsCount: 0,
+ supportPercent: 100,
+ shouldFormatted: '26 of 26 passed',
+ testsFailedCount: 0,
+ testsPassedCount: 18,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 117,
+ assertionsFailedCount: 0,
+ assertionsPassedCount: 143,
+ shouldAssertionsCount: 26,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 117,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 26,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 26,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 26
+ },
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
+ },
+ testPlanReport: {
+ id: '9'
+ },
+ testResults: [
+ {
+ test: {
+ id: 'ZjE0NeyIyIjoiMjQifQmI0NT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.229Z'
+ },
+ {
+ test: {
+ id: 'YjZlNeyIyIjoiMjQifQTc5ZW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.339Z'
+ },
+ {
+ test: {
+ id: 'NWM0MeyIyIjoiMjQifQzhiYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.439Z'
+ },
+ {
+ test: {
+ id: 'YzM0ZeyIyIjoiMjQifQmRmMz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.537Z'
+ },
+ {
+ test: {
+ id: 'ZjVjMeyIyIjoiMjQifQDRhY2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.636Z'
+ },
+ {
+ test: {
+ id: 'YmUzMeyIyIjoiMjQifQmRmNm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.736Z'
+ },
+ {
+ test: {
+ id: 'ZGJmMeyIyIjoiMjQifQzU5Yz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.824Z'
+ },
+ {
+ test: {
+ id: 'Nzg1OeyIyIjoiMjQifQTYxM2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.901Z'
+ },
+ {
+ test: {
+ id: 'ZTI0MeyIyIjoiMjQifQzM4YT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:48.978Z'
+ },
+ {
+ test: {
+ id: 'MzY5ZeyIyIjoiMjQifQmQ0OT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.070Z'
+ },
+ {
+ test: {
+ id: 'ZjVmYeyIyIjoiMjQifQjJjYW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.161Z'
+ },
+ {
+ test: {
+ id: 'ZTMwNeyIyIjoiMjQifQzI5Nz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.264Z'
+ },
+ {
+ test: {
+ id: 'NGY0MeyIyIjoiMjQifQ2FjMj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.387Z'
+ },
+ {
+ test: {
+ id: 'OTI0OeyIyIjoiMjQifQTU1ZT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.527Z'
+ },
+ {
+ test: {
+ id: 'MDRhMeyIyIjoiMjQifQWEzMj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.646Z'
+ },
+ {
+ test: {
+ id: 'ZThlZeyIyIjoiMjQifQjQ2Nz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.747Z'
+ },
+ {
+ test: {
+ id: 'NjhjMeyIyIjoiMjQifQGE0ND'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.847Z'
+ },
+ {
+ test: {
+ id: 'YTAzZeyIyIjoiMjQifQTc5ZD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:49.951Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '5',
+ metrics: {
+ testsCount: 11,
+ mayFormatted: false,
+ supportLevel: 'FAILING',
+ commandsCount: 20,
+ mustFormatted: '88 of 90 passed',
+ conflictsCount: 0,
+ supportPercent: 98,
+ shouldFormatted: '19 of 20 passed',
+ testsFailedCount: 2,
+ testsPassedCount: 9,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 90,
+ assertionsFailedCount: 3,
+ assertionsPassedCount: 107,
+ shouldAssertionsCount: 20,
+ unexpectedBehaviorCount: 1,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 88,
+ shouldAssertionsFailedCount: 1,
+ shouldAssertionsPassedCount: 19,
+ unexpectedBehaviorsFormatted: '1 found',
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 20,
+ moderateImpactFailedAssertionCount: 1,
+ moderateImpactPassedAssertionCount: 19
+ },
+ isFinal: true,
+ markedFinalAt: '2022-07-06T00:00:00.000Z',
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
+ },
+ testPlanReport: {
+ id: '5'
+ },
+ testResults: [
+ {
+ test: {
+ id: 'NjM0MeyIyIjoiMjQifQTdiZG'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:44.402Z'
+ },
+ {
+ test: {
+ id: 'YWYzOeyIyIjoiMjQifQDBjN2'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:44.489Z'
+ },
+ {
+ test: {
+ id: 'ZmJjYeyIyIjoiMjQifQWJiNT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:44.594Z'
+ },
+ {
+ test: {
+ id: 'MjU2NeyIyIjoiMjQifQTk2YW'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:44.694Z'
+ },
+ {
+ test: {
+ id: 'MWNlNeyIyIjoiMjQifQTRhNT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:44.781Z'
+ },
+ {
+ test: {
+ id: 'YzFlYeyIyIjoiMjQifQjE5Yj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:44.877Z'
+ },
+ {
+ test: {
+ id: 'N2UwMeyIyIjoiMjQifQTQ1OT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:44.966Z'
+ },
+ {
+ test: {
+ id: 'OTYwOeyIyIjoiMjQifQTE3ND'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.061Z'
+ },
+ {
+ test: {
+ id: 'OWI2MeyIyIjoiMjQifQmE0ZD'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.165Z'
+ },
+ {
+ test: {
+ id: 'YTU0MeyIyIjoiMjQifQjNhNj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.296Z'
+ },
+ {
+ test: {
+ id: 'NTM4MeyIyIjoiMjQifQGVlNm'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:45.433Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '11',
+ metrics: {
+ testsCount: 11,
+ mayFormatted: false,
+ supportLevel: 'FULL',
+ commandsCount: 20,
+ mustFormatted: '90 of 90 passed',
+ conflictsCount: 0,
+ supportPercent: 100,
+ shouldFormatted: '20 of 20 passed',
+ testsFailedCount: 0,
+ testsPassedCount: 11,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 90,
+ assertionsFailedCount: 0,
+ assertionsPassedCount: 110,
+ shouldAssertionsCount: 20,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 90,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 20,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 20,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 20
+ },
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- id: '33',
- title: 'Tabs with Manual Activation',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'tabs-manual-activation'
- },
- testPlanReports: [],
- metadata: {}
+ testPlanReport: {
+ id: '11'
},
- {
- id: '35',
- title: 'Vertical Temperature Slider',
- phase: 'RD',
- gitSha: '7c4b5dce23c74fcf280ed164bdb903e02e0e7726',
- gitMessage:
- 'Generate html source script to support aria-at-app (#646)',
- updatedAt: '2022-03-17T18:34:51.000Z',
- versionString: 'V22.03.17',
- draftPhaseReachedAt: null,
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'vertical-temperature-slider'
- },
- testPlanReports: [],
- metadata: {}
+ testResults: [
+ {
+ test: {
+ id: 'NjM0MeyIyIjoiMjQifQTdiZG'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.034Z'
+ },
+ {
+ test: {
+ id: 'YWYzOeyIyIjoiMjQifQDBjN2'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.120Z'
+ },
+ {
+ test: {
+ id: 'ZmJjYeyIyIjoiMjQifQWJiNT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.205Z'
+ },
+ {
+ test: {
+ id: 'MjU2NeyIyIjoiMjQifQTk2YW'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.313Z'
+ },
+ {
+ test: {
+ id: 'MWNlNeyIyIjoiMjQifQTRhNT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.410Z'
+ },
+ {
+ test: {
+ id: 'YzFlYeyIyIjoiMjQifQjE5Yj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.489Z'
+ },
+ {
+ test: {
+ id: 'N2UwMeyIyIjoiMjQifQTQ1OT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.561Z'
+ },
+ {
+ test: {
+ id: 'OTYwOeyIyIjoiMjQifQTE3ND'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.648Z'
+ },
+ {
+ test: {
+ id: 'OWI2MeyIyIjoiMjQifQmE0ZD'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.736Z'
+ },
+ {
+ test: {
+ id: 'YTU0MeyIyIjoiMjQifQjNhNj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.832Z'
+ },
+ {
+ test: {
+ id: 'NTM4MeyIyIjoiMjQifQGVlNm'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:51.954Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '10',
+ metrics: {
+ testsCount: 11,
+ mayFormatted: false,
+ supportLevel: 'FULL',
+ commandsCount: 20,
+ mustFormatted: '90 of 90 passed',
+ conflictsCount: 0,
+ supportPercent: 100,
+ shouldFormatted: '20 of 20 passed',
+ testsFailedCount: 0,
+ testsPassedCount: 11,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 90,
+ assertionsFailedCount: 0,
+ assertionsPassedCount: 110,
+ shouldAssertionsCount: 20,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 90,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 20,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 20,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 20
+ },
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- {
- 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',
- versionString: 'V22.10.24',
- 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'
- },
- testPlanReports: [
- {
- id: '6',
- metrics: {
- testsCount: 7,
- supportLevel: 'FAILING',
- conflictsCount: 0,
- supportPercent: 96,
- testsFailedCount: 3,
- testsPassedCount: 4,
- shouldFormatted: '4 of 4 passed',
- mustFormatted: '44 of 46 passed',
- shouldAssertionsCount: 4,
- mustAssertionsCount: 46,
- unexpectedBehaviorCount: 0,
- unexpectedBehaviorsFormatted: false,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 4,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 44
- },
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- isFinal: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'tom-proudfeet'
- },
- testPlanReport: {
- id: '6'
- },
- testResults: [
- {
- test: {
- id: 'YTE3NeyIyIjoiNSJ9WJlMj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.341Z'
- },
- {
- test: {
- id: 'YWJiOeyIyIjoiNSJ9GQ5Zm'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.405Z'
- },
- {
- test: {
- id: 'ZGFlYeyIyIjoiNSJ9TJlMW'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.474Z'
- },
- {
- test: {
- id: 'YjI2MeyIyIjoiNSJ9WE1OT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.537Z'
- },
- {
- test: {
- id: 'ZjAwZeyIyIjoiNSJ9TZmZj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.605Z'
- },
- {
- test: {
- id: 'MGRjZeyIyIjoiNSJ9WNiZD'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.670Z'
- },
- {
- test: {
- id: 'OTZmYeyIyIjoiNSJ9TU5Ym'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.739Z'
- }
- ]
- }
- ]
- },
- {
- id: '12',
- metrics: {
- testsCount: 14,
- supportLevel: 'FULL',
- conflictsCount: 0,
- supportPercent: 100,
- testsFailedCount: 12,
- testsPassedCount: 2,
- shouldFormatted: false,
- mustFormatted: '25 of 25 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 25,
- unexpectedBehaviorCount: 0,
- unexpectedBehaviorsFormatted: false,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 25
- },
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '12'
- },
- testResults: [
- {
- test: {
- id: 'MTVlZeyIyIjoiNSJ9DUzMz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:11.764Z'
- },
- {
- test: {
- id: 'OThhMeyIyIjoiNSJ9WMxM2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:11.828Z'
- },
- {
- test: {
- id: 'YWNhNeyIyIjoiNSJ9TliN2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:11.892Z'
- }
- ]
- }
- ]
- },
- {
- id: '13',
- metrics: {
- testsCount: 14,
- supportLevel: 'FULL',
- conflictsCount: 0,
- supportPercent: 100,
- testsFailedCount: 12,
- testsPassedCount: 2,
- shouldFormatted: false,
- mustFormatted: '25 of 25 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 25,
- unexpectedBehaviorCount: 0,
- unexpectedBehaviorsFormatted: false,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 25
- },
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '13'
- },
- testResults: [
- {
- test: {
- id: 'MTVlZeyIyIjoiNSJ9DUzMz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:11.955Z'
- },
- {
- test: {
- id: 'OThhMeyIyIjoiNSJ9WMxM2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:12.017Z'
- },
- {
- test: {
- id: 'YWNhNeyIyIjoiNSJ9TliN2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:12.083Z'
- }
- ]
- }
- ]
- }
- ],
- metadata: {}
+ testPlanReport: {
+ id: '10'
},
- {
- 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',
- versionString: 'V22.06.22',
- draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
- candidatePhaseReachedAt: '2022-07-06T00:00:00.000Z',
- recommendedPhaseTargetDate: '2023-01-02T00:00:00.000Z',
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'modal-dialog'
- },
- testPlanReports: [
- {
- id: '10',
- metrics: {
- testsCount: 11,
- supportLevel: 'FULL',
- conflictsCount: 0,
- supportPercent: 100,
- testsFailedCount: 9,
- testsPassedCount: 2,
- shouldFormatted: false,
- mustFormatted: '14 of 14 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 14,
- unexpectedBehaviorCount: 0,
- unexpectedBehaviorsFormatted: false,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 14
- },
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '10'
- },
- testResults: [
- {
- test: {
- id: 'MzlmYeyIyIjoiMjYifQzIxY2'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:11.295Z'
- },
- {
- test: {
- id: 'N2FkZeyIyIjoiMjYifQDQ5NT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:11.369Z'
- },
- {
- test: {
- id: 'ZDJkYeyIyIjoiMjYifQzRkYj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:11.450Z'
- }
- ]
- }
- ]
- },
- {
- id: '9',
- metrics: {
- testsCount: 18,
- supportLevel: 'FULL',
- conflictsCount: 0,
- supportPercent: 100,
- testsFailedCount: 16,
- testsPassedCount: 2,
- shouldFormatted: false,
- mustFormatted: '16 of 16 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 16,
- unexpectedBehaviorCount: 0,
- unexpectedBehaviorsFormatted: false,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 16
- },
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '9'
- },
- testResults: [
- {
- test: {
- id: 'MThhNeyIyIjoiMjYifQmEyMj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:11.059Z'
- },
- {
- test: {
- id: 'ODY5MeyIyIjoiMjYifQzhmNW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:11.137Z'
- },
- {
- test: {
- id: 'NWVkNeyIyIjoiMjYifQTZkOT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:11.218Z'
- }
- ]
- }
- ]
- },
- {
- id: '3',
- metrics: {
- testsCount: 18,
- supportLevel: 'FAILING',
- conflictsCount: 0,
- supportPercent: 88,
- testsFailedCount: 16,
- testsPassedCount: 2,
- shouldFormatted: false,
- mustFormatted: '14 of 16 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 16,
- unexpectedBehaviorCount: 1,
- unexpectedBehaviorsFormatted: '1 found',
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 14
- },
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- isFinal: true,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '3'
- },
- testResults: [
- {
- test: {
- id: 'MThhNeyIyIjoiMjYifQmEyMj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.074Z'
- },
- {
- test: {
- id: 'NWVkNeyIyIjoiMjYifQTZkOT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.134Z'
- },
- {
- test: {
- id: 'NWM4NeyIyIjoiMjYifQDEwM2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.202Z'
- },
- {
- test: {
- id: 'NGFiZeyIyIjoiMjYifQWZiYW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.268Z'
- },
- {
- test: {
- id: 'MzQzYeyIyIjoiMjYifQzU5Zm'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.336Z'
- }
- ]
- }
- ]
- },
- {
- id: '11',
- metrics: {
- testsCount: 11,
- supportLevel: 'FULL',
- conflictsCount: 0,
- supportPercent: 100,
- testsFailedCount: 9,
- testsPassedCount: 2,
- shouldFormatted: false,
- mustFormatted: '14 of 14 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 14,
- unexpectedBehaviorCount: 0,
- unexpectedBehaviorsFormatted: false,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 14
- },
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '11'
- },
- testResults: [
- {
- test: {
- id: 'MzlmYeyIyIjoiMjYifQzIxY2'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:11.532Z'
- },
- {
- test: {
- id: 'N2FkZeyIyIjoiMjYifQDQ5NT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:11.611Z'
- },
- {
- test: {
- id: 'ZDJkYeyIyIjoiMjYifQzRkYj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:11.696Z'
- }
- ]
- }
- ]
- },
- {
- id: '4',
- metrics: {
- testsCount: 18,
- supportLevel: 'FAILING',
- conflictsCount: 0,
- supportPercent: 91,
- testsFailedCount: 14,
- testsPassedCount: 4,
- shouldFormatted: false,
- mustFormatted: '20 of 22 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 22,
- unexpectedBehaviorCount: 1,
- unexpectedBehaviorsFormatted: '1 found',
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 20
- },
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- isFinal: true,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '4'
- },
- testResults: [
- {
- test: {
- id: 'MThhNeyIyIjoiMjYifQmEyMj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.409Z'
- },
- {
- test: {
- id: 'NWVkNeyIyIjoiMjYifQTZkOT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.478Z'
- },
- {
- test: {
- id: 'NWM4NeyIyIjoiMjYifQDEwM2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.551Z'
- },
- {
- test: {
- id: 'NGFiZeyIyIjoiMjYifQWZiYW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.629Z'
- },
- {
- test: {
- id: 'MzQzYeyIyIjoiMjYifQzU5Zm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.704Z'
- },
- {
- test: {
- id: 'MmI1MeyIyIjoiMjYifQmU3Yz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.777Z'
- },
- {
- test: {
- id: 'YmRmYeyIyIjoiMjYifQjEyMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2023-08-18T03:17:09.852Z'
- }
- ]
- }
- ]
- },
- {
- id: '5',
- metrics: {
- testsCount: 11,
- supportLevel: 'FAILING',
- conflictsCount: 0,
- supportPercent: 92,
- testsFailedCount: 8,
- testsPassedCount: 3,
- shouldFormatted: false,
- mustFormatted: '23 of 25 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 25,
- unexpectedBehaviorCount: 1,
- unexpectedBehaviorsFormatted: '1 found',
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 23
- },
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- isFinal: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '5'
- },
- testResults: [
- {
- test: {
- id: 'MzlmYeyIyIjoiMjYifQzIxY2'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:09.923Z'
- },
- {
- test: {
- id: 'ZDJkYeyIyIjoiMjYifQzRkYj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:09.991Z'
- },
- {
- test: {
- id: 'ZmQyNeyIyIjoiMjYifQ2M2ND'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.059Z'
- },
- {
- test: {
- id: 'OGE3YeyIyIjoiMjYifQjU1ND'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.129Z'
- },
- {
- test: {
- id: 'YWI3OeyIyIjoiMjYifQWJlNW'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.198Z'
- },
- {
- test: {
- id: 'M2RiOeyIyIjoiMjYifQGY1Nj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2023-08-18T03:17:10.272Z'
- }
- ]
- }
- ]
- },
- {
- id: '8',
- metrics: {
- testsCount: 18,
- supportLevel: 'FULL',
- conflictsCount: 0,
- supportPercent: 100,
- testsFailedCount: 16,
- testsPassedCount: 2,
- shouldFormatted: false,
- mustFormatted: '16 of 16 passed',
- shouldAssertionsCount: 0,
- mustAssertionsCount: 16,
- unexpectedBehaviorCount: 0,
- unexpectedBehaviorsFormatted: false,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 16
- },
- markedFinalAt: null,
- isFinal: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '8'
- },
- testResults: [
- {
- test: {
- id: 'MThhNeyIyIjoiMjYifQmEyMj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:10.817Z'
- },
- {
- test: {
- id: 'ODY5MeyIyIjoiMjYifQzhmNW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:10.894Z'
- },
- {
- test: {
- id: 'NWVkNeyIyIjoiMjYifQTZkOT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2023-08-18T03:17:10.979Z'
- }
- ]
- }
- ]
- }
- ],
- metadata: {}
- }
+ testResults: [
+ {
+ test: {
+ id: 'NjM0MeyIyIjoiMjQifQTdiZG'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.048Z'
+ },
+ {
+ test: {
+ id: 'YWYzOeyIyIjoiMjQifQDBjN2'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.128Z'
+ },
+ {
+ test: {
+ id: 'ZmJjYeyIyIjoiMjQifQWJiNT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.203Z'
+ },
+ {
+ test: {
+ id: 'MjU2NeyIyIjoiMjQifQTk2YW'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.278Z'
+ },
+ {
+ test: {
+ id: 'MWNlNeyIyIjoiMjQifQTRhNT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.355Z'
+ },
+ {
+ test: {
+ id: 'YzFlYeyIyIjoiMjQifQjE5Yj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.426Z'
+ },
+ {
+ test: {
+ id: 'N2UwMeyIyIjoiMjQifQTQ1OT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.508Z'
+ },
+ {
+ test: {
+ id: 'OTYwOeyIyIjoiMjQifQTE3ND'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.591Z'
+ },
+ {
+ test: {
+ id: 'OWI2MeyIyIjoiMjQifQmE0ZD'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.687Z'
+ },
+ {
+ test: {
+ id: 'YTU0MeyIyIjoiMjQifQjNhNj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.788Z'
+ },
+ {
+ test: {
+ id: 'NTM4MeyIyIjoiMjQifQGVlNm'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:50.914Z'
+ }
+ ]
+ }
]
+ }
}
+ ]
}
+ }
+ }
+ },
+ {
+ request: {
+ query: testPlanReportStatusDialogQuery,
+ variables: { testPlanVersionId: '34' }
},
- {
- request: {
- query: testPlanReportStatusDialogQuery,
- variables: { testPlanVersionId: '1' }
- },
- result: {
- data: {
- testPlanVersion: {
+ result: {
+ data: {
+ testPlanVersion: {
+ id: '31',
+ title: 'Toggle Button',
+ phase: 'DRAFT',
+ gitSha: '022340081280b8cafb8ae0716a5b67e9ab942ef4',
+ gitMessage:
+ '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: 'toggle-button'
+ },
+ testPlanReportStatuses: [
+ {
+ isRequired: true,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
id: '1',
- title: 'Alert Example',
- phase: 'DRAFT',
- gitSha: 'c665367f3742c2b607f7b3c2655782188b93f302',
- gitMessage:
- 'Create updated tests for APG design pattern example: Alert (#685)',
- updatedAt: '2022-04-14T17:59:42.000Z',
- draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
- candidatePhaseReachedAt: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'alert'
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '1',
+ metrics: {
+ testsCount: 16,
+ mayFormatted: false,
+ supportLevel: 'FAILING',
+ commandsCount: 26,
+ mustFormatted: '86 of 88 passed',
+ conflictsCount: 0,
+ supportPercent: 98,
+ shouldFormatted: '25 of 26 passed',
+ testsFailedCount: 6,
+ testsPassedCount: 10,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 88,
+ assertionsFailedCount: 3,
+ assertionsPassedCount: 111,
+ shouldAssertionsCount: 26,
+ unexpectedBehaviorCount: 1,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 2,
+ mustAssertionsPassedCount: 86,
+ shouldAssertionsFailedCount: 1,
+ shouldAssertionsPassedCount: 25,
+ unexpectedBehaviorsFormatted: '1 found',
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 26,
+ moderateImpactFailedAssertionCount: 1,
+ moderateImpactPassedAssertionCount: 25
+ },
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- testPlanReportStatuses: [
- {
- isRequired: true,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '101',
- metrics: {},
- isFinal: false,
- markedFinalAt: null,
- issues: [],
- draftTestPlanRuns: []
- }
- },
- {
- isRequired: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '1',
- key: 'chrome',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '7',
- metrics: {},
- isFinal: false,
- markedFinalAt: null,
- issues: [],
- draftTestPlanRuns: []
- }
- }
- ]
- }
- }
- }
- },
- {
- request: {
- query: testPlanReportStatusDialogQuery,
- variables: { testPlanVersionId: '5' }
- },
- result: {
- data: {
- testPlanVersion: {
- id: '69',
- title: 'Checkbox Example (Mixed-State)',
- phase: 'RECOMMENDED',
- gitSha: '836fb2a997f5b2844035b8c934f8fda9833cd5b2',
- gitMessage: 'Validation for test csv formats (#980)',
- updatedAt: '2023-08-23T20:30:34.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'
+ testPlanReport: {
+ id: '1'
},
- testPlanReportStatuses: [
- {
- isRequired: true,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '12',
- metrics: {
- testsCount: 14,
- mayFormatted: false,
- supportLevel: 'FULL',
- commandsCount: 26,
- mustFormatted: '116 of 116 passed',
- conflictsCount: 0,
- supportPercent: 100,
- shouldFormatted: '34 of 34 passed',
- testsFailedCount: 0,
- testsPassedCount: 14,
- mayAssertionsCount: 0,
- mustAssertionsCount: 116,
- assertionsFailedCount: 0,
- assertionsPassedCount: 150,
- shouldAssertionsCount: 34,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 116,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 34,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 26,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 26
- },
- isFinal: true,
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '12'
- },
- testResults: [
- {
- test: {
- id: 'YWYzOeyIyIjoiNjkifQTQ0MT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.053Z'
- },
- {
- test: {
- id: 'OGZjNeyIyIjoiNjkifQjQxZW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.149Z'
- },
- {
- test: {
- id: 'NjM3ZeyIyIjoiNjkifQmUxYz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.242Z'
- },
- {
- test: {
- id: 'ZWQ0MeyIyIjoiNjkifQGZhYT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.321Z'
- },
- {
- test: {
- id: 'ZGI3ZeyIyIjoiNjkifQTc5Mj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.409Z'
- },
- {
- test: {
- id: 'MDZjOeyIyIjoiNjkifQGJkYz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.510Z'
- },
- {
- test: {
- id: 'ZmI3NeyIyIjoiNjkifQzUwMT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.614Z'
- },
- {
- test: {
- id: 'NmY2YeyIyIjoiNjkifQTczOW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.731Z'
- },
- {
- test: {
- id: 'MjIwYeyIyIjoiNjkifQmUzZj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:52.882Z'
- },
- {
- test: {
- id: 'ODg0OeyIyIjoiNjkifQWFlYm'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:53.038Z'
- },
- {
- test: {
- id: 'ZDQ2MeyIyIjoiNjkifQjlmZj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:53.184Z'
- },
- {
- test: {
- id: 'MjdlYeyIyIjoiNjkifQTgyNj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:53.317Z'
- },
- {
- test: {
- id: 'OGE5MeyIyIjoiNjkifQGZjOT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:53.428Z'
- },
- {
- test: {
- id: 'YWNlNeyIyIjoiNjkifQjQzOW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:53.567Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: true,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '13',
- metrics: {
- testsCount: 14,
- mayFormatted: false,
- supportLevel: 'FULL',
- commandsCount: 28,
- mustFormatted: '124 of 124 passed',
- conflictsCount: 0,
- supportPercent: 100,
- shouldFormatted: '36 of 36 passed',
- testsFailedCount: 0,
- testsPassedCount: 14,
- mayAssertionsCount: 0,
- mustAssertionsCount: 124,
- assertionsFailedCount: 0,
- assertionsPassedCount: 160,
- shouldAssertionsCount: 36,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 124,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 36,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 28,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 28
- },
- isFinal: true,
- markedFinalAt: '2022-07-07T00:00:00.000Z',
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '13'
- },
- testResults: [
- {
- test: {
- id: 'YWYzOeyIyIjoiNjkifQTQ0MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:53.698Z'
- },
- {
- test: {
- id: 'OGZjNeyIyIjoiNjkifQjQxZW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:53.814Z'
- },
- {
- test: {
- id: 'NjM3ZeyIyIjoiNjkifQmUxYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:53.921Z'
- },
- {
- test: {
- id: 'ZWQ0MeyIyIjoiNjkifQGZhYT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.038Z'
- },
- {
- test: {
- id: 'ZGI3ZeyIyIjoiNjkifQTc5Mj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.181Z'
- },
- {
- test: {
- id: 'MDZjOeyIyIjoiNjkifQGJkYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.327Z'
- },
- {
- test: {
- id: 'ZmI3NeyIyIjoiNjkifQzUwMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.421Z'
- },
- {
- test: {
- id: 'NmY2YeyIyIjoiNjkifQTczOW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.541Z'
- },
- {
- test: {
- id: 'MjIwYeyIyIjoiNjkifQmUzZj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.666Z'
- },
- {
- test: {
- id: 'ODg0OeyIyIjoiNjkifQWFlYm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.794Z'
- },
- {
- test: {
- id: 'ZDQ2MeyIyIjoiNjkifQjlmZj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.881Z'
- },
- {
- test: {
- id: 'MjdlYeyIyIjoiNjkifQTgyNj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:54.967Z'
- },
- {
- test: {
- id: 'OGE5MeyIyIjoiNjkifQGZjOT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.059Z'
- },
- {
- test: {
- id: 'YWNlNeyIyIjoiNjkifQjQzOW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.140Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: true,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '6',
- metrics: {
- testsCount: 7,
- mayFormatted: false,
- supportLevel: 'FAILING',
- commandsCount: 16,
- mustFormatted: '66 of 68 passed',
- conflictsCount: 0,
- supportPercent: 97,
- shouldFormatted: '20 of 20 passed',
- testsFailedCount: 2,
- testsPassedCount: 5,
- mayAssertionsCount: 0,
- mustAssertionsCount: 68,
- assertionsFailedCount: 2,
- assertionsPassedCount: 86,
- shouldAssertionsCount: 20,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 66,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 20,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 16,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 16
- },
- isFinal: true,
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'tom-proudfeet'
- },
- testPlanReport: {
- id: '6'
- },
- testResults: [
- {
- test: {
- id: 'NmUzMeyIyIjoiNjkifQmU0OT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.554Z'
- },
- {
- test: {
- id: 'Y2UyYeyIyIjoiNjkifQ2Y1Mz'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.671Z'
- },
- {
- test: {
- id: 'ODc2OeyIyIjoiNjkifQTA1Yz'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.794Z'
- },
- {
- test: {
- id: 'OTgwZeyIyIjoiNjkifQDZjOG'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.909Z'
- },
- {
- test: {
- id: 'ODA3ZeyIyIjoiNjkifQjI4Y2'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.997Z'
- },
- {
- test: {
- id: 'OWI4MeyIyIjoiNjkifQzFlZD'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:46.091Z'
- },
- {
- test: {
- id: 'MzhiZeyIyIjoiNjkifQWE4Nj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:46.189Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- }
+ testResults: [
+ {
+ test: {
+ id: 'MTExZeyIyIjoiMzEifQWZhZG'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:35.281Z'
+ },
+ {
+ test: {
+ id: 'MzJkZeyIyIjoiMzEifQTAzMm'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'NDBjMeyIyIjoiMzEifQjc1NT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'MjE2MeyIyIjoiMzEifQ2M0NW'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'MWZiZeyIyIjoiMzEifQjhhYz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:35.541Z'
+ },
+ {
+ test: {
+ id: 'NmI4NeyIyIjoiMzEifQDU2OD'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:35.636Z'
+ },
+ {
+ test: {
+ id: 'YmExNeyIyIjoiMzEifQWE5Nj'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:35.730Z'
+ },
+ {
+ test: {
+ id: 'YzA3NeyIyIjoiMzEifQGZhYT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:35.831Z'
+ },
+ {
+ test: {
+ id: 'YmYxOeyIyIjoiMzEifQDAxY2'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:35.928Z'
+ },
+ {
+ test: {
+ id: 'YzIwOeyIyIjoiMzEifQGE2Yz'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:36.034Z'
+ },
+ {
+ test: {
+ id: 'YWMwNeyIyIjoiMzEifQDQ5MG'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:36.126Z'
+ },
+ {
+ test: {
+ id: 'MjQyMeyIyIjoiMzEifQWExMm'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:36.210Z'
+ },
+ {
+ test: {
+ id: 'NDFiYeyIyIjoiMzEifQzg4MD'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:36.332Z'
+ },
+ {
+ test: {
+ id: 'M2RmNeyIyIjoiMzEifQzQ0MG'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:36.409Z'
+ },
+ {
+ test: {
+ id: 'ODhlYeyIyIjoiMzEifQmVmMT'
+ },
+ atVersion: {
+ id: '1',
+ name: '2021.2111.13'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:36.510Z'
+ }
]
- }
- }
- }
- },
- {
- request: {
- query: testPlanReportStatusDialogQuery,
- variables: { testPlanVersionId: '7' }
- },
- result: {
- data: {
- testPlanVersion: {
- id: '7',
- title: 'Select Only Combobox Example',
- phase: 'DRAFT',
- 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: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'combobox-select-only'
+ }
+ ]
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '14',
+ metrics: {
+ testsCount: 16,
+ mayFormatted: false,
+ supportLevel: 'FULL',
+ commandsCount: 36,
+ mustFormatted: '128 of 128 passed',
+ conflictsCount: 0,
+ supportPercent: 100,
+ shouldFormatted: '36 of 36 passed',
+ testsFailedCount: 0,
+ testsPassedCount: 16,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 128,
+ assertionsFailedCount: 0,
+ assertionsPassedCount: 164,
+ shouldAssertionsCount: 36,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 128,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 36,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 36,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 36
+ },
+ isFinal: true,
+ markedFinalAt: '2022-07-07T00:00:00.000Z',
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- testPlanReportStatuses: [
- {
- isRequired: true,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '2',
- metrics: {
- testsCount: 21,
- mayFormatted: false,
- supportLevel: 'FAILING',
- commandsCount: 24,
- mustFormatted: '118 of 122 passed',
- conflictsCount: 3,
- supportPercent: 97,
- shouldFormatted: '34 of 36 passed',
- testsFailedCount: 6,
- testsPassedCount: 15,
- mayAssertionsCount: 0,
- mustAssertionsCount: 122,
- assertionsFailedCount: 6,
- assertionsPassedCount: 152,
- shouldAssertionsCount: 36,
- unexpectedBehaviorCount: 3,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 4,
- mustAssertionsPassedCount: 118,
- shouldAssertionsFailedCount: 2,
- shouldAssertionsPassedCount: 34,
- unexpectedBehaviorsFormatted: '3 found',
- severeImpactFailedAssertionCount: 1,
- severeImpactPassedAssertionCount: 23,
- moderateImpactFailedAssertionCount: 2,
- moderateImpactPassedAssertionCount: 22
- },
- isFinal: false,
- markedFinalAt: null,
- issues: [
- {
- link: 'https://github.com/bocoup/aria-at/issues/128#issue-2157878584',
- isOpen: true,
- feedbackType: 'FEEDBACK'
- }
- ],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'tom-proudfeet'
- },
- testPlanReport: {
- id: '2'
- },
- testResults: [
- {
- test: {
- id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.949Z'
- },
- {
- test: {
- id: 'MmY0YeyIyIjoiNyJ9jRkZD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:39.070Z'
- },
- {
- test: {
- id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:39.180Z'
- },
- {
- test: {
- id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:39.292Z'
- },
- {
- test: {
- id: 'MjRmNeyIyIjoiNyJ92MyMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:39.406Z'
- },
- {
- test: {
- id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: null
- },
- {
- test: {
- id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:39.640Z'
- },
- {
- test: {
- id: 'YjZkYeyIyIjoiNyJ9WIxZm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:39.761Z'
- },
- {
- test: {
- id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:39.888Z'
- },
- {
- test: {
- id: 'MmZkNeyIyIjoiNyJ9zIwN2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.006Z'
- },
- {
- test: {
- id: 'ZmQwOeyIyIjoiNyJ9DEzYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.122Z'
- },
- {
- test: {
- id: 'MGViNeyIyIjoiNyJ9GQ3MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.234Z'
- },
- {
- test: {
- id: 'YTg5MeyIyIjoiNyJ9WEzOT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.355Z'
- },
- {
- test: {
- id: 'NTRjMeyIyIjoiNyJ9zQ0OD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.467Z'
- },
- {
- test: {
- id: 'MjRlZeyIyIjoiNyJ9DcyY2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.588Z'
- },
- {
- test: {
- id: 'YWQzNeyIyIjoiNyJ9mE2Nm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.712Z'
- },
- {
- test: {
- id: 'OTYxOeyIyIjoiNyJ9TdmYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.826Z'
- },
- {
- test: {
- id: 'MjgzNeyIyIjoiNyJ9TZjNz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:40.948Z'
- },
- {
- test: {
- id: 'NWNiZeyIyIjoiNyJ9jI2MD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:41.075Z'
- }
- ]
- },
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '2'
- },
- testResults: [
- {
- test: {
- id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:36.666Z'
- },
- {
- test: {
- id: 'MmY0YeyIyIjoiNyJ9jRkZD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:36.793Z'
- },
- {
- test: {
- id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:36.914Z'
- },
- {
- test: {
- id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:37.031Z'
- },
- {
- test: {
- id: 'MjRmNeyIyIjoiNyJ92MyMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:37.150Z'
- },
- {
- test: {
- id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: null
- },
- {
- test: {
- id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:37.384Z'
- },
- {
- test: {
- id: 'YjZkYeyIyIjoiNyJ9WIxZm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:37.512Z'
- },
- {
- test: {
- id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:37.638Z'
- },
- {
- test: {
- id: 'MmZkNeyIyIjoiNyJ9zIwN2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:37.760Z'
- },
- {
- test: {
- id: 'ZmQwOeyIyIjoiNyJ9DEzYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:37.883Z'
- },
- {
- test: {
- id: 'MGViNeyIyIjoiNyJ9GQ3MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.014Z'
- },
- {
- test: {
- id: 'YTg5MeyIyIjoiNyJ9WEzOT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.145Z'
- },
- {
- test: {
- id: 'NTRjMeyIyIjoiNyJ9zQ0OD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.268Z'
- },
- {
- test: {
- id: 'MjRlZeyIyIjoiNyJ9DcyY2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.382Z'
- },
- {
- test: {
- id: 'YWQzNeyIyIjoiNyJ9mE2Nm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.481Z'
- },
- {
- test: {
- id: 'OTYxOeyIyIjoiNyJ9TdmYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.596Z'
- },
- {
- test: {
- id: 'MjgzNeyIyIjoiNyJ9TZjNz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.701Z'
- },
- {
- test: {
- id: 'NWNiZeyIyIjoiNyJ9jI2MD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:38.811Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- }
- ]
- }
- }
- }
- },
- {
- request: {
- query: testPlanReportStatusDialogQuery,
- variables: { testPlanVersionId: '26' }
- },
- result: {
- data: {
- testPlanVersion: {
- id: '24',
- title: 'Modal Dialog Example',
- phase: 'CANDIDATE',
- gitSha: '5fe7afd82fe51c185b8661276105190a59d47322',
- gitMessage: 'Task 7: delete incorrect instructions (#733)',
- updatedAt: '2022-05-26T16:10:10.000Z',
- draftPhaseReachedAt: '2022-07-06T00:00:00.000Z',
- candidatePhaseReachedAt: '2022-07-06T00:00:00.000Z',
- recommendedPhaseTargetDate: '2023-01-02T00:00:00.000Z',
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'modal-dialog'
+ testPlanReport: {
+ id: '14'
},
- testPlanReportStatuses: [
- {
- isRequired: true,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '3',
- metrics: {
- testsCount: 18,
- mayFormatted: false,
- supportLevel: 'FAILING',
- commandsCount: 26,
- mustFormatted: '115 of 117 passed',
- conflictsCount: 0,
- supportPercent: 98,
- shouldFormatted: '25 of 26 passed',
- testsFailedCount: 2,
- testsPassedCount: 16,
- mayAssertionsCount: 0,
- mustAssertionsCount: 117,
- assertionsFailedCount: 3,
- assertionsPassedCount: 140,
- shouldAssertionsCount: 26,
- unexpectedBehaviorCount: 1,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 115,
- shouldAssertionsFailedCount: 1,
- shouldAssertionsPassedCount: 25,
- unexpectedBehaviorsFormatted: '1 found',
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 26,
- moderateImpactFailedAssertionCount: 1,
- moderateImpactPassedAssertionCount: 25
- },
- isFinal: true,
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '3'
- },
- testResults: [
- {
- test: {
- id: 'ZjE0NeyIyIjoiMjQifQmI0NT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.177Z'
- },
- {
- test: {
- id: 'YjZlNeyIyIjoiMjQifQTc5ZW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.256Z'
- },
- {
- test: {
- id: 'NWM0MeyIyIjoiMjQifQzhiYz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.349Z'
- },
- {
- test: {
- id: 'YzM0ZeyIyIjoiMjQifQmRmMz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.419Z'
- },
- {
- test: {
- id: 'ZjVjMeyIyIjoiMjQifQDRhY2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.496Z'
- },
- {
- test: {
- id: 'YmUzMeyIyIjoiMjQifQmRmNm'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.574Z'
- },
- {
- test: {
- id: 'ZGJmMeyIyIjoiMjQifQzU5Yz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.661Z'
- },
- {
- test: {
- id: 'Nzg1OeyIyIjoiMjQifQTYxM2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.741Z'
- },
- {
- test: {
- id: 'ZTI0MeyIyIjoiMjQifQzM4YT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.823Z'
- },
- {
- test: {
- id: 'MzY5ZeyIyIjoiMjQifQmQ0OT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:41.950Z'
- },
- {
- test: {
- id: 'ZjVmYeyIyIjoiMjQifQjJjYW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.044Z'
- },
- {
- test: {
- id: 'ZTMwNeyIyIjoiMjQifQzI5Nz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.112Z'
- },
- {
- test: {
- id: 'NGY0MeyIyIjoiMjQifQ2FjMj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.191Z'
- },
- {
- test: {
- id: 'OTI0OeyIyIjoiMjQifQTU1ZT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.271Z'
- },
- {
- test: {
- id: 'MDRhMeyIyIjoiMjQifQWEzMj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.344Z'
- },
- {
- test: {
- id: 'ZThlZeyIyIjoiMjQifQjQ2Nz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.412Z'
- },
- {
- test: {
- id: 'NjhjMeyIyIjoiMjQifQGE0ND'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.482Z'
- },
- {
- test: {
- id: 'YTAzZeyIyIjoiMjQifQTc5ZD'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.575Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: null,
- exactAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- testPlanReport: {
- id: '105',
- metrics: {},
- isFinal: false,
- markedFinalAt: null,
- issues: [],
- draftTestPlanRuns: []
- }
- },
- {
- isRequired: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '8',
- metrics: {
- testsCount: 18,
- mayFormatted: false,
- supportLevel: 'FULL',
- commandsCount: 26,
- mustFormatted: '117 of 117 passed',
- conflictsCount: 0,
- supportPercent: 100,
- shouldFormatted: '26 of 26 passed',
- testsFailedCount: 0,
- testsPassedCount: 18,
- mayAssertionsCount: 0,
- mustAssertionsCount: 117,
- assertionsFailedCount: 0,
- assertionsPassedCount: 143,
- shouldAssertionsCount: 26,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 117,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 26,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 26,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 26
- },
- isFinal: false,
- markedFinalAt: null,
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '8'
- },
- testResults: [
- {
- test: {
- id: 'ZjE0NeyIyIjoiMjQifQmI0NT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.300Z'
- },
- {
- test: {
- id: 'YjZlNeyIyIjoiMjQifQTc5ZW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.396Z'
- },
- {
- test: {
- id: 'NWM0MeyIyIjoiMjQifQzhiYz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.490Z'
- },
- {
- test: {
- id: 'YzM0ZeyIyIjoiMjQifQmRmMz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.572Z'
- },
- {
- test: {
- id: 'ZjVjMeyIyIjoiMjQifQDRhY2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.656Z'
- },
- {
- test: {
- id: 'YmUzMeyIyIjoiMjQifQmRmNm'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.743Z'
- },
- {
- test: {
- id: 'ZGJmMeyIyIjoiMjQifQzU5Yz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.828Z'
- },
- {
- test: {
- id: 'Nzg1OeyIyIjoiMjQifQTYxM2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.912Z'
- },
- {
- test: {
- id: 'ZTI0MeyIyIjoiMjQifQzM4YT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:46.990Z'
- },
- {
- test: {
- id: 'MzY5ZeyIyIjoiMjQifQmQ0OT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:47.087Z'
- },
- {
- test: {
- id: 'ZjVmYeyIyIjoiMjQifQjJjYW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:47.215Z'
- },
- {
- test: {
- id: 'ZTMwNeyIyIjoiMjQifQzI5Nz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:47.355Z'
- },
- {
- test: {
- id: 'NGY0MeyIyIjoiMjQifQ2FjMj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:47.484Z'
- },
- {
- test: {
- id: 'OTI0OeyIyIjoiMjQifQTU1ZT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:47.614Z'
- },
- {
- test: {
- id: 'MDRhMeyIyIjoiMjQifQWEzMj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:47.738Z'
- },
- {
- test: {
- id: 'ZThlZeyIyIjoiMjQifQjQ2Nz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:47.862Z'
- },
- {
- test: {
- id: 'NjhjMeyIyIjoiMjQifQGE0ND'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:47.981Z'
- },
- {
- test: {
- id: 'YTAzZeyIyIjoiMjQifQTc5ZD'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.105Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: true,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '4',
- metrics: {
- testsCount: 18,
- mayFormatted: false,
- supportLevel: 'FAILING',
- commandsCount: 26,
- mustFormatted: '115 of 117 passed',
- conflictsCount: 0,
- supportPercent: 98,
- shouldFormatted: '25 of 26 passed',
- testsFailedCount: 2,
- testsPassedCount: 16,
- mayAssertionsCount: 0,
- mustAssertionsCount: 117,
- assertionsFailedCount: 3,
- assertionsPassedCount: 140,
- shouldAssertionsCount: 26,
- unexpectedBehaviorCount: 1,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 115,
- shouldAssertionsFailedCount: 1,
- shouldAssertionsPassedCount: 25,
- unexpectedBehaviorsFormatted: '1 found',
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 26,
- moderateImpactFailedAssertionCount: 1,
- moderateImpactPassedAssertionCount: 25
- },
- isFinal: true,
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '4'
- },
- testResults: [
- {
- test: {
- id: 'ZjE0NeyIyIjoiMjQifQmI0NT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.683Z'
- },
- {
- test: {
- id: 'YjZlNeyIyIjoiMjQifQTc5ZW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.772Z'
- },
- {
- test: {
- id: 'NWM0MeyIyIjoiMjQifQzhiYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.848Z'
- },
- {
- test: {
- id: 'YzM0ZeyIyIjoiMjQifQmRmMz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:42.928Z'
- },
- {
- test: {
- id: 'ZjVjMeyIyIjoiMjQifQDRhY2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.005Z'
- },
- {
- test: {
- id: 'YmUzMeyIyIjoiMjQifQmRmNm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.093Z'
- },
- {
- test: {
- id: 'ZGJmMeyIyIjoiMjQifQzU5Yz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.173Z'
- },
- {
- test: {
- id: 'Nzg1OeyIyIjoiMjQifQTYxM2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.245Z'
- },
- {
- test: {
- id: 'ZTI0MeyIyIjoiMjQifQzM4YT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.318Z'
- },
- {
- test: {
- id: 'MzY5ZeyIyIjoiMjQifQmQ0OT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.399Z'
- },
- {
- test: {
- id: 'ZjVmYeyIyIjoiMjQifQjJjYW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.473Z'
- },
- {
- test: {
- id: 'ZTMwNeyIyIjoiMjQifQzI5Nz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.554Z'
- },
- {
- test: {
- id: 'NGY0MeyIyIjoiMjQifQ2FjMj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.643Z'
- },
- {
- test: {
- id: 'OTI0OeyIyIjoiMjQifQTU1ZT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.738Z'
- },
- {
- test: {
- id: 'MDRhMeyIyIjoiMjQifQWEzMj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.844Z'
- },
- {
- test: {
- id: 'ZThlZeyIyIjoiMjQifQjQ2Nz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:43.950Z'
- },
- {
- test: {
- id: 'NjhjMeyIyIjoiMjQifQGE0ND'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:44.078Z'
- },
- {
- test: {
- id: 'YTAzZeyIyIjoiMjQifQTc5ZD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:44.219Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: false,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '9',
- metrics: {
- testsCount: 18,
- mayFormatted: false,
- supportLevel: 'FULL',
- commandsCount: 26,
- mustFormatted: '117 of 117 passed',
- conflictsCount: 0,
- supportPercent: 100,
- shouldFormatted: '26 of 26 passed',
- testsFailedCount: 0,
- testsPassedCount: 18,
- mayAssertionsCount: 0,
- mustAssertionsCount: 117,
- assertionsFailedCount: 0,
- assertionsPassedCount: 143,
- shouldAssertionsCount: 26,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 117,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 26,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 26,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 26
- },
- isFinal: false,
- markedFinalAt: null,
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '9'
- },
- testResults: [
- {
- test: {
- id: 'ZjE0NeyIyIjoiMjQifQmI0NT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.229Z'
- },
- {
- test: {
- id: 'YjZlNeyIyIjoiMjQifQTc5ZW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.339Z'
- },
- {
- test: {
- id: 'NWM0MeyIyIjoiMjQifQzhiYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.439Z'
- },
- {
- test: {
- id: 'YzM0ZeyIyIjoiMjQifQmRmMz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.537Z'
- },
- {
- test: {
- id: 'ZjVjMeyIyIjoiMjQifQDRhY2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.636Z'
- },
- {
- test: {
- id: 'YmUzMeyIyIjoiMjQifQmRmNm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.736Z'
- },
- {
- test: {
- id: 'ZGJmMeyIyIjoiMjQifQzU5Yz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.824Z'
- },
- {
- test: {
- id: 'Nzg1OeyIyIjoiMjQifQTYxM2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.901Z'
- },
- {
- test: {
- id: 'ZTI0MeyIyIjoiMjQifQzM4YT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:48.978Z'
- },
- {
- test: {
- id: 'MzY5ZeyIyIjoiMjQifQmQ0OT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.070Z'
- },
- {
- test: {
- id: 'ZjVmYeyIyIjoiMjQifQjJjYW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.161Z'
- },
- {
- test: {
- id: 'ZTMwNeyIyIjoiMjQifQzI5Nz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.264Z'
- },
- {
- test: {
- id: 'NGY0MeyIyIjoiMjQifQ2FjMj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.387Z'
- },
- {
- test: {
- id: 'OTI0OeyIyIjoiMjQifQTU1ZT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.527Z'
- },
- {
- test: {
- id: 'MDRhMeyIyIjoiMjQifQWEzMj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.646Z'
- },
- {
- test: {
- id: 'ZThlZeyIyIjoiMjQifQjQ2Nz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.747Z'
- },
- {
- test: {
- id: 'NjhjMeyIyIjoiMjQifQGE0ND'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.847Z'
- },
- {
- test: {
- id: 'YTAzZeyIyIjoiMjQifQTc5ZD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:49.951Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '5',
- metrics: {
- testsCount: 11,
- mayFormatted: false,
- supportLevel: 'FAILING',
- commandsCount: 20,
- mustFormatted: '88 of 90 passed',
- conflictsCount: 0,
- supportPercent: 98,
- shouldFormatted: '19 of 20 passed',
- testsFailedCount: 2,
- testsPassedCount: 9,
- mayAssertionsCount: 0,
- mustAssertionsCount: 90,
- assertionsFailedCount: 3,
- assertionsPassedCount: 107,
- shouldAssertionsCount: 20,
- unexpectedBehaviorCount: 1,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 88,
- shouldAssertionsFailedCount: 1,
- shouldAssertionsPassedCount: 19,
- unexpectedBehaviorsFormatted: '1 found',
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 20,
- moderateImpactFailedAssertionCount: 1,
- moderateImpactPassedAssertionCount: 19
- },
- isFinal: true,
- markedFinalAt: '2022-07-06T00:00:00.000Z',
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '5'
- },
- testResults: [
- {
- test: {
- id: 'NjM0MeyIyIjoiMjQifQTdiZG'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:44.402Z'
- },
- {
- test: {
- id: 'YWYzOeyIyIjoiMjQifQDBjN2'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:44.489Z'
- },
- {
- test: {
- id: 'ZmJjYeyIyIjoiMjQifQWJiNT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:44.594Z'
- },
- {
- test: {
- id: 'MjU2NeyIyIjoiMjQifQTk2YW'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:44.694Z'
- },
- {
- test: {
- id: 'MWNlNeyIyIjoiMjQifQTRhNT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:44.781Z'
- },
- {
- test: {
- id: 'YzFlYeyIyIjoiMjQifQjE5Yj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:44.877Z'
- },
- {
- test: {
- id: 'N2UwMeyIyIjoiMjQifQTQ1OT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:44.966Z'
- },
- {
- test: {
- id: 'OTYwOeyIyIjoiMjQifQTE3ND'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.061Z'
- },
- {
- test: {
- id: 'OWI2MeyIyIjoiMjQifQmE0ZD'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.165Z'
- },
- {
- test: {
- id: 'YTU0MeyIyIjoiMjQifQjNhNj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.296Z'
- },
- {
- test: {
- id: 'NTM4MeyIyIjoiMjQifQGVlNm'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:45.433Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '11',
- metrics: {
- testsCount: 11,
- mayFormatted: false,
- supportLevel: 'FULL',
- commandsCount: 20,
- mustFormatted: '90 of 90 passed',
- conflictsCount: 0,
- supportPercent: 100,
- shouldFormatted: '20 of 20 passed',
- testsFailedCount: 0,
- testsPassedCount: 11,
- mayAssertionsCount: 0,
- mustAssertionsCount: 90,
- assertionsFailedCount: 0,
- assertionsPassedCount: 110,
- shouldAssertionsCount: 20,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 90,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 20,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 20,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 20
- },
- isFinal: false,
- markedFinalAt: null,
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '11'
- },
- testResults: [
- {
- test: {
- id: 'NjM0MeyIyIjoiMjQifQTdiZG'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.034Z'
- },
- {
- test: {
- id: 'YWYzOeyIyIjoiMjQifQDBjN2'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.120Z'
- },
- {
- test: {
- id: 'ZmJjYeyIyIjoiMjQifQWJiNT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.205Z'
- },
- {
- test: {
- id: 'MjU2NeyIyIjoiMjQifQTk2YW'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.313Z'
- },
- {
- test: {
- id: 'MWNlNeyIyIjoiMjQifQTRhNT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.410Z'
- },
- {
- test: {
- id: 'YzFlYeyIyIjoiMjQifQjE5Yj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.489Z'
- },
- {
- test: {
- id: 'N2UwMeyIyIjoiMjQifQTQ1OT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.561Z'
- },
- {
- test: {
- id: 'OTYwOeyIyIjoiMjQifQTE3ND'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.648Z'
- },
- {
- test: {
- id: 'OWI2MeyIyIjoiMjQifQmE0ZD'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.736Z'
- },
- {
- test: {
- id: 'YTU0MeyIyIjoiMjQifQjNhNj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.832Z'
- },
- {
- test: {
- id: 'NTM4MeyIyIjoiMjQifQGVlNm'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:51.954Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '10',
- metrics: {
- testsCount: 11,
- mayFormatted: false,
- supportLevel: 'FULL',
- commandsCount: 20,
- mustFormatted: '90 of 90 passed',
- conflictsCount: 0,
- supportPercent: 100,
- shouldFormatted: '20 of 20 passed',
- testsFailedCount: 0,
- testsPassedCount: 11,
- mayAssertionsCount: 0,
- mustAssertionsCount: 90,
- assertionsFailedCount: 0,
- assertionsPassedCount: 110,
- shouldAssertionsCount: 20,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 90,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 20,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 20,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 20
- },
- isFinal: false,
- markedFinalAt: null,
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '10'
- },
- testResults: [
- {
- test: {
- id: 'NjM0MeyIyIjoiMjQifQTdiZG'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.048Z'
- },
- {
- test: {
- id: 'YWYzOeyIyIjoiMjQifQDBjN2'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.128Z'
- },
- {
- test: {
- id: 'ZmJjYeyIyIjoiMjQifQWJiNT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.203Z'
- },
- {
- test: {
- id: 'MjU2NeyIyIjoiMjQifQTk2YW'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.278Z'
- },
- {
- test: {
- id: 'MWNlNeyIyIjoiMjQifQTRhNT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.355Z'
- },
- {
- test: {
- id: 'YzFlYeyIyIjoiMjQifQjE5Yj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.426Z'
- },
- {
- test: {
- id: 'N2UwMeyIyIjoiMjQifQTQ1OT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.508Z'
- },
- {
- test: {
- id: 'OTYwOeyIyIjoiMjQifQTE3ND'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.591Z'
- },
- {
- test: {
- id: 'OWI2MeyIyIjoiMjQifQmE0ZD'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.687Z'
- },
- {
- test: {
- id: 'YTU0MeyIyIjoiMjQifQjNhNj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.788Z'
- },
- {
- test: {
- id: 'NTM4MeyIyIjoiMjQifQGVlNm'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt:
- '2024-04-25T16:44:50.914Z'
- }
- ]
- }
- ]
- }
- }
+ testResults: [
+ {
+ test: {
+ id: 'MTExZeyIyIjoiMzEifQWZhZG'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.237Z'
+ },
+ {
+ test: {
+ id: 'MzJkZeyIyIjoiMzEifQTAzMm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.347Z'
+ },
+ {
+ test: {
+ id: 'NDBjMeyIyIjoiMzEifQjc1NT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.423Z'
+ },
+ {
+ test: {
+ id: 'MjE2MeyIyIjoiMzEifQ2M0NW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.518Z'
+ },
+ {
+ test: {
+ id: 'MmZmNeyIyIjoiMzEifQ2IxOG'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.613Z'
+ },
+ {
+ test: {
+ id: 'MWZiZeyIyIjoiMzEifQjhhYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.721Z'
+ },
+ {
+ test: {
+ id: 'NmI4NeyIyIjoiMzEifQDU2OD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.838Z'
+ },
+ {
+ test: {
+ id: 'YmExNeyIyIjoiMzEifQWE5Nj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:55.965Z'
+ },
+ {
+ test: {
+ id: 'YzA3NeyIyIjoiMzEifQGZhYT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:56.079Z'
+ },
+ {
+ test: {
+ id: 'YmYxOeyIyIjoiMzEifQDAxY2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:56.177Z'
+ },
+ {
+ test: {
+ id: 'YzIwOeyIyIjoiMzEifQGE2Yz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:56.286Z'
+ },
+ {
+ test: {
+ id: 'YWMwNeyIyIjoiMzEifQDQ5MG'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:56.389Z'
+ },
+ {
+ test: {
+ id: 'MjQyMeyIyIjoiMzEifQWExMm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:56.509Z'
+ },
+ {
+ test: {
+ id: 'NDFiYeyIyIjoiMzEifQzg4MD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:56.639Z'
+ },
+ {
+ test: {
+ id: 'M2RmNeyIyIjoiMzEifQzQ0MG'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:56.783Z'
+ },
+ {
+ test: {
+ id: 'ODhlYeyIyIjoiMzEifQmVmMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '2',
+ name: '99.0.4844.84'
+ },
+ completedAt: '2024-04-25T16:44:56.935Z'
+ }
]
- }
- }
- }
- },
- {
- request: {
- query: testPlanReportStatusDialogQuery,
- variables: { testPlanVersionId: '34' }
- },
- result: {
- data: {
- testPlanVersion: {
- id: '31',
- title: 'Toggle Button',
- phase: 'DRAFT',
- gitSha: '022340081280b8cafb8ae0716a5b67e9ab942ef4',
- gitMessage:
- '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: 'toggle-button'
+ }
+ ]
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '15',
+ metrics: {
+ testsCount: 8,
+ mayFormatted: false,
+ supportLevel: 'FULL',
+ commandsCount: 22,
+ mustFormatted: '76 of 76 passed',
+ conflictsCount: 0,
+ supportPercent: 100,
+ shouldFormatted: '22 of 22 passed',
+ testsFailedCount: 0,
+ testsPassedCount: 8,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 76,
+ assertionsFailedCount: 0,
+ assertionsPassedCount: 98,
+ shouldAssertionsCount: 22,
+ unexpectedBehaviorCount: 0,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 0,
+ mustAssertionsPassedCount: 76,
+ shouldAssertionsFailedCount: 0,
+ shouldAssertionsPassedCount: 22,
+ unexpectedBehaviorsFormatted: false,
+ severeImpactFailedAssertionCount: 0,
+ severeImpactPassedAssertionCount: 22,
+ moderateImpactFailedAssertionCount: 0,
+ moderateImpactPassedAssertionCount: 22
+ },
+ isFinal: true,
+ markedFinalAt: '2022-07-07T00:00:00.000Z',
+ issues: [],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- testPlanReportStatuses: [
- {
- isRequired: true,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '1',
- metrics: {
- testsCount: 16,
- mayFormatted: false,
- supportLevel: 'FAILING',
- commandsCount: 26,
- mustFormatted: '86 of 88 passed',
- conflictsCount: 0,
- supportPercent: 98,
- shouldFormatted: '25 of 26 passed',
- testsFailedCount: 6,
- testsPassedCount: 10,
- mayAssertionsCount: 0,
- mustAssertionsCount: 88,
- assertionsFailedCount: 3,
- assertionsPassedCount: 111,
- shouldAssertionsCount: 26,
- unexpectedBehaviorCount: 1,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 2,
- mustAssertionsPassedCount: 86,
- shouldAssertionsFailedCount: 1,
- shouldAssertionsPassedCount: 25,
- unexpectedBehaviorsFormatted: '1 found',
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 26,
- moderateImpactFailedAssertionCount: 1,
- moderateImpactPassedAssertionCount: 25
- },
- isFinal: false,
- markedFinalAt: null,
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '1'
- },
- testResults: [
- {
- test: {
- id: 'MTExZeyIyIjoiMzEifQWZhZG'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:35.281Z'
- },
- {
- test: {
- id: 'MzJkZeyIyIjoiMzEifQTAzMm'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt: null
- },
- {
- test: {
- id: 'NDBjMeyIyIjoiMzEifQjc1NT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt: null
- },
- {
- test: {
- id: 'MjE2MeyIyIjoiMzEifQ2M0NW'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt: null
- },
- {
- test: {
- id: 'MWZiZeyIyIjoiMzEifQjhhYz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:35.541Z'
- },
- {
- test: {
- id: 'NmI4NeyIyIjoiMzEifQDU2OD'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:35.636Z'
- },
- {
- test: {
- id: 'YmExNeyIyIjoiMzEifQWE5Nj'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:35.730Z'
- },
- {
- test: {
- id: 'YzA3NeyIyIjoiMzEifQGZhYT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:35.831Z'
- },
- {
- test: {
- id: 'YmYxOeyIyIjoiMzEifQDAxY2'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:35.928Z'
- },
- {
- test: {
- id: 'YzIwOeyIyIjoiMzEifQGE2Yz'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:36.034Z'
- },
- {
- test: {
- id: 'YWMwNeyIyIjoiMzEifQDQ5MG'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:36.126Z'
- },
- {
- test: {
- id: 'MjQyMeyIyIjoiMzEifQWExMm'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:36.210Z'
- },
- {
- test: {
- id: 'NDFiYeyIyIjoiMzEifQzg4MD'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:36.332Z'
- },
- {
- test: {
- id: 'M2RmNeyIyIjoiMzEifQzQ0MG'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:36.409Z'
- },
- {
- test: {
- id: 'ODhlYeyIyIjoiMzEifQmVmMT'
- },
- atVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:36.510Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '14',
- metrics: {
- testsCount: 16,
- mayFormatted: false,
- supportLevel: 'FULL',
- commandsCount: 36,
- mustFormatted: '128 of 128 passed',
- conflictsCount: 0,
- supportPercent: 100,
- shouldFormatted: '36 of 36 passed',
- testsFailedCount: 0,
- testsPassedCount: 16,
- mayAssertionsCount: 0,
- mustAssertionsCount: 128,
- assertionsFailedCount: 0,
- assertionsPassedCount: 164,
- shouldAssertionsCount: 36,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 128,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 36,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 36,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 36
- },
- isFinal: true,
- markedFinalAt: '2022-07-07T00:00:00.000Z',
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '14'
- },
- testResults: [
- {
- test: {
- id: 'MTExZeyIyIjoiMzEifQWZhZG'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.237Z'
- },
- {
- test: {
- id: 'MzJkZeyIyIjoiMzEifQTAzMm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.347Z'
- },
- {
- test: {
- id: 'NDBjMeyIyIjoiMzEifQjc1NT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.423Z'
- },
- {
- test: {
- id: 'MjE2MeyIyIjoiMzEifQ2M0NW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.518Z'
- },
- {
- test: {
- id: 'MmZmNeyIyIjoiMzEifQ2IxOG'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.613Z'
- },
- {
- test: {
- id: 'MWZiZeyIyIjoiMzEifQjhhYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.721Z'
- },
- {
- test: {
- id: 'NmI4NeyIyIjoiMzEifQDU2OD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.838Z'
- },
- {
- test: {
- id: 'YmExNeyIyIjoiMzEifQWE5Nj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:55.965Z'
- },
- {
- test: {
- id: 'YzA3NeyIyIjoiMzEifQGZhYT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:56.079Z'
- },
- {
- test: {
- id: 'YmYxOeyIyIjoiMzEifQDAxY2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:56.177Z'
- },
- {
- test: {
- id: 'YzIwOeyIyIjoiMzEifQGE2Yz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:56.286Z'
- },
- {
- test: {
- id: 'YWMwNeyIyIjoiMzEifQDQ5MG'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:56.389Z'
- },
- {
- test: {
- id: 'MjQyMeyIyIjoiMzEifQWExMm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:56.509Z'
- },
- {
- test: {
- id: 'NDFiYeyIyIjoiMzEifQzg4MD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:56.639Z'
- },
- {
- test: {
- id: 'M2RmNeyIyIjoiMzEifQzQ0MG'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:56.783Z'
- },
- {
- test: {
- id: 'ODhlYeyIyIjoiMzEifQmVmMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '2',
- name: '99.0.4844.84'
- },
- completedAt:
- '2024-04-25T16:44:56.935Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: false,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: {
- id: '15',
- metrics: {
- testsCount: 8,
- mayFormatted: false,
- supportLevel: 'FULL',
- commandsCount: 22,
- mustFormatted: '76 of 76 passed',
- conflictsCount: 0,
- supportPercent: 100,
- shouldFormatted: '22 of 22 passed',
- testsFailedCount: 0,
- testsPassedCount: 8,
- mayAssertionsCount: 0,
- mustAssertionsCount: 76,
- assertionsFailedCount: 0,
- assertionsPassedCount: 98,
- shouldAssertionsCount: 22,
- unexpectedBehaviorCount: 0,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 0,
- mustAssertionsPassedCount: 76,
- shouldAssertionsFailedCount: 0,
- shouldAssertionsPassedCount: 22,
- unexpectedBehaviorsFormatted: false,
- severeImpactFailedAssertionCount: 0,
- severeImpactPassedAssertionCount: 22,
- moderateImpactFailedAssertionCount: 0,
- moderateImpactPassedAssertionCount: 22
- },
- isFinal: true,
- markedFinalAt: '2022-07-07T00:00:00.000Z',
- issues: [],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '15'
- },
- testResults: [
- {
- test: {
- id: 'NWUyZeyIyIjoiMzEifQDVkND'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:57.086Z'
- },
- {
- test: {
- id: 'N2I0YeyIyIjoiMzEifQjEwYj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:57.206Z'
- },
- {
- test: {
- id: 'NmZjOeyIyIjoiMzEifQGY5ZT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:57.334Z'
- },
- {
- test: {
- id: 'YmU1MeyIyIjoiMzEifQzBiYj'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:57.464Z'
- },
- {
- test: {
- id: 'MGQyYeyIyIjoiMzEifQzcxZm'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:57.599Z'
- },
- {
- test: {
- id: 'ZmI0YeyIyIjoiMzEifQTU2Nz'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:57.734Z'
- },
- {
- test: {
- id: 'YmRjZeyIyIjoiMzEifQGQyND'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:57.880Z'
- },
- {
- test: {
- id: 'YWFmMeyIyIjoiMzEifQzMwMT'
- },
- atVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- browserVersion: {
- id: '3',
- name: '14.1.2'
- },
- completedAt:
- '2024-04-25T16:44:58.035Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- }
+ testPlanReport: {
+ id: '15'
+ },
+ testResults: [
+ {
+ test: {
+ id: 'NWUyZeyIyIjoiMzEifQDVkND'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:57.086Z'
+ },
+ {
+ test: {
+ id: 'N2I0YeyIyIjoiMzEifQjEwYj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:57.206Z'
+ },
+ {
+ test: {
+ id: 'NmZjOeyIyIjoiMzEifQGY5ZT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:57.334Z'
+ },
+ {
+ test: {
+ id: 'YmU1MeyIyIjoiMzEifQzBiYj'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:57.464Z'
+ },
+ {
+ test: {
+ id: 'MGQyYeyIyIjoiMzEifQzcxZm'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:57.599Z'
+ },
+ {
+ test: {
+ id: 'ZmI0YeyIyIjoiMzEifQTU2Nz'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:57.734Z'
+ },
+ {
+ test: {
+ id: 'YmRjZeyIyIjoiMzEifQGQyND'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:57.880Z'
+ },
+ {
+ test: {
+ id: 'YWFmMeyIyIjoiMzEifQzMwMT'
+ },
+ atVersion: {
+ id: '3',
+ name: '11.6 (20G165)'
+ },
+ browserVersion: {
+ id: '3',
+ name: '14.1.2'
+ },
+ completedAt: '2024-04-25T16:44:58.035Z'
+ }
]
- }
+ }
+ ]
+ }
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
}
+ ]
}
+ }
}
+ }
];
diff --git a/client/tests/__mocks__/GraphQLMocks/TestPlanReportStatusDialogMock.js b/client/tests/__mocks__/GraphQLMocks/TestPlanReportStatusDialogMock.js
index 32a9a824b..84015446a 100644
--- a/client/tests/__mocks__/GraphQLMocks/TestPlanReportStatusDialogMock.js
+++ b/client/tests/__mocks__/GraphQLMocks/TestPlanReportStatusDialogMock.js
@@ -1,818 +1,888 @@
export const mockedTestPlanVersion = {
- id: '7',
- title: 'Select Only Combobox Example',
- phase: 'DRAFT',
- 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: null,
- recommendedPhaseTargetDate: null,
- recommendedPhaseReachedAt: null,
- testPlan: {
- directory: 'combobox-select-only'
+ id: '7',
+ title: 'Select Only Combobox Example',
+ phase: 'DRAFT',
+ 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: null,
+ recommendedPhaseTargetDate: null,
+ recommendedPhaseReachedAt: null,
+ testPlan: {
+ directory: 'combobox-select-only'
+ },
+ testPlanReportStatuses: [
+ {
+ isRequired: true,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
},
- testPlanReportStatuses: [
- {
- isRequired: true,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- minimumAtVersion: {
- id: '1',
- name: '2021.2111.13'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: true,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
- testPlanReport: null
+ {
+ isRequired: false,
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS',
+ atVersions: [
+ {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '1',
+ name: '2021.2111.13',
+ supportedByAutomation: false,
+ releasedAt: '2021-11-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA',
+ atVersions: [
+ {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '2',
+ name: '2020.4',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: {
+ id: '2',
+ metrics: {
+ testsCount: 21,
+ mayFormatted: false,
+ supportLevel: 'FAILING',
+ commandsCount: 24,
+ mustFormatted: '118 of 122 passed',
+ conflictsCount: 3,
+ supportPercent: 97,
+ shouldFormatted: '34 of 36 passed',
+ testsFailedCount: 6,
+ testsPassedCount: 15,
+ mayAssertionsCount: 0,
+ mustAssertionsCount: 122,
+ assertionsFailedCount: 6,
+ assertionsPassedCount: 152,
+ shouldAssertionsCount: 36,
+ unexpectedBehaviorCount: 3,
+ mayAssertionsFailedCount: 0,
+ mayAssertionsPassedCount: 0,
+ mustAssertionsFailedCount: 4,
+ mustAssertionsPassedCount: 118,
+ shouldAssertionsFailedCount: 2,
+ shouldAssertionsPassedCount: 34,
+ unexpectedBehaviorsFormatted: '3 found',
+ severeImpactFailedAssertionCount: 1,
+ severeImpactPassedAssertionCount: 23,
+ moderateImpactFailedAssertionCount: 2,
+ moderateImpactPassedAssertionCount: 22
},
- {
- isRequired: false,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
+ isFinal: false,
+ markedFinalAt: null,
+ issues: [
+ {
+ link: 'https://github.com/bocoup/aria-at/issues/128#issue-2157878584',
+ isOpen: true,
+ feedbackType: 'FEEDBACK'
+ }
+ ],
+ draftTestPlanRuns: [
+ {
+ tester: {
+ username: 'tom-proudfeet'
},
- minimumAtVersion: {
- id: '2',
- name: '2020.4'
- },
- exactAtVersion: null,
testPlanReport: {
- id: '2',
- metrics: {
- testsCount: 21,
- mayFormatted: false,
- supportLevel: 'FAILING',
- commandsCount: 24,
- mustFormatted: '118 of 122 passed',
- conflictsCount: 3,
- supportPercent: 97,
- shouldFormatted: '34 of 36 passed',
- testsFailedCount: 6,
- testsPassedCount: 15,
- mayAssertionsCount: 0,
- mustAssertionsCount: 122,
- assertionsFailedCount: 6,
- assertionsPassedCount: 152,
- shouldAssertionsCount: 36,
- unexpectedBehaviorCount: 3,
- mayAssertionsFailedCount: 0,
- mayAssertionsPassedCount: 0,
- mustAssertionsFailedCount: 4,
- mustAssertionsPassedCount: 118,
- shouldAssertionsFailedCount: 2,
- shouldAssertionsPassedCount: 34,
- unexpectedBehaviorsFormatted: '3 found',
- severeImpactFailedAssertionCount: 1,
- severeImpactPassedAssertionCount: 23,
- moderateImpactFailedAssertionCount: 2,
- moderateImpactPassedAssertionCount: 22
- },
- isFinal: false,
- markedFinalAt: null,
- issues: [
- {
- link: 'https://github.com/bocoup/aria-at/issues/128#issue-2157878584',
- isOpen: true,
- feedbackType: 'FEEDBACK'
- }
- ],
- draftTestPlanRuns: [
- {
- tester: {
- username: 'tom-proudfeet'
- },
- testPlanReport: {
- id: '2'
- },
- testResults: [
- {
- test: {
- id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.949Z'
- },
- {
- test: {
- id: 'MmY0YeyIyIjoiNyJ9jRkZD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:39.070Z'
- },
- {
- test: {
- id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:39.180Z'
- },
- {
- test: {
- id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:39.292Z'
- },
- {
- test: {
- id: 'MjRmNeyIyIjoiNyJ92MyMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:39.406Z'
- },
- {
- test: {
- id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: null
- },
- {
- test: {
- id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:39.640Z'
- },
- {
- test: {
- id: 'YjZkYeyIyIjoiNyJ9WIxZm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:39.761Z'
- },
- {
- test: {
- id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:39.888Z'
- },
- {
- test: {
- id: 'MmZkNeyIyIjoiNyJ9zIwN2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.006Z'
- },
- {
- test: {
- id: 'ZmQwOeyIyIjoiNyJ9DEzYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.122Z'
- },
- {
- test: {
- id: 'MGViNeyIyIjoiNyJ9GQ3MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.234Z'
- },
- {
- test: {
- id: 'YTg5MeyIyIjoiNyJ9WEzOT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.355Z'
- },
- {
- test: {
- id: 'NTRjMeyIyIjoiNyJ9zQ0OD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.467Z'
- },
- {
- test: {
- id: 'MjRlZeyIyIjoiNyJ9DcyY2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.588Z'
- },
- {
- test: {
- id: 'YWQzNeyIyIjoiNyJ9mE2Nm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.712Z'
- },
- {
- test: {
- id: 'OTYxOeyIyIjoiNyJ9TdmYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.826Z'
- },
- {
- test: {
- id: 'MjgzNeyIyIjoiNyJ9TZjNz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:40.948Z'
- },
- {
- test: {
- id: 'NWNiZeyIyIjoiNyJ9jI2MD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:41.075Z'
- }
- ]
- },
- {
- tester: {
- username: 'esmeralda-baggins'
- },
- testPlanReport: {
- id: '2'
- },
- testResults: [
- {
- test: {
- id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:36.666Z'
- },
- {
- test: {
- id: 'MmY0YeyIyIjoiNyJ9jRkZD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:36.793Z'
- },
- {
- test: {
- id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:36.914Z'
- },
- {
- test: {
- id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:37.031Z'
- },
- {
- test: {
- id: 'MjRmNeyIyIjoiNyJ92MyMT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:37.150Z'
- },
- {
- test: {
- id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: null
- },
- {
- test: {
- id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:37.384Z'
- },
- {
- test: {
- id: 'YjZkYeyIyIjoiNyJ9WIxZm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:37.512Z'
- },
- {
- test: {
- id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:37.638Z'
- },
- {
- test: {
- id: 'MmZkNeyIyIjoiNyJ9zIwN2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:37.760Z'
- },
- {
- test: {
- id: 'ZmQwOeyIyIjoiNyJ9DEzYz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:37.883Z'
- },
- {
- test: {
- id: 'MGViNeyIyIjoiNyJ9GQ3MT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.014Z'
- },
- {
- test: {
- id: 'YTg5MeyIyIjoiNyJ9WEzOT'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.145Z'
- },
- {
- test: {
- id: 'NTRjMeyIyIjoiNyJ9zQ0OD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.268Z'
- },
- {
- test: {
- id: 'MjRlZeyIyIjoiNyJ9DcyY2'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.382Z'
- },
- {
- test: {
- id: 'YWQzNeyIyIjoiNyJ9mE2Nm'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.481Z'
- },
- {
- test: {
- id: 'OTYxOeyIyIjoiNyJ9TdmYj'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.596Z'
- },
- {
- test: {
- id: 'MjgzNeyIyIjoiNyJ9TZjNz'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.701Z'
- },
- {
- test: {
- id: 'NWNiZeyIyIjoiNyJ9jI2MD'
- },
- atVersion: {
- id: '2',
- name: '2020.4'
- },
- browserVersion: {
- id: '1',
- name: '99.0.1'
- },
- completedAt: '2024-04-25T16:44:38.811Z'
- }
- ]
- }
- ]
- }
- },
- {
- isRequired: true,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
- },
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
+ id: '2'
},
- exactAtVersion: null,
- testPlanReport: null
- },
- {
- isRequired: false,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
+ testResults: [
+ {
+ test: {
+ id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.949Z'
+ },
+ {
+ test: {
+ id: 'MmY0YeyIyIjoiNyJ9jRkZD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.070Z'
+ },
+ {
+ test: {
+ id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.180Z'
+ },
+ {
+ test: {
+ id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.292Z'
+ },
+ {
+ test: {
+ id: 'MjRmNeyIyIjoiNyJ92MyMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.406Z'
+ },
+ {
+ test: {
+ id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.640Z'
+ },
+ {
+ test: {
+ id: 'YjZkYeyIyIjoiNyJ9WIxZm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.761Z'
+ },
+ {
+ test: {
+ id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:39.888Z'
+ },
+ {
+ test: {
+ id: 'MmZkNeyIyIjoiNyJ9zIwN2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.006Z'
+ },
+ {
+ test: {
+ id: 'ZmQwOeyIyIjoiNyJ9DEzYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.122Z'
+ },
+ {
+ test: {
+ id: 'MGViNeyIyIjoiNyJ9GQ3MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.234Z'
+ },
+ {
+ test: {
+ id: 'YTg5MeyIyIjoiNyJ9WEzOT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.355Z'
+ },
+ {
+ test: {
+ id: 'NTRjMeyIyIjoiNyJ9zQ0OD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.467Z'
+ },
+ {
+ test: {
+ id: 'MjRlZeyIyIjoiNyJ9DcyY2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.588Z'
+ },
+ {
+ test: {
+ id: 'YWQzNeyIyIjoiNyJ9mE2Nm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.712Z'
+ },
+ {
+ test: {
+ id: 'OTYxOeyIyIjoiNyJ9TdmYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.826Z'
+ },
+ {
+ test: {
+ id: 'MjgzNeyIyIjoiNyJ9TZjNz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:40.948Z'
+ },
+ {
+ test: {
+ id: 'NWNiZeyIyIjoiNyJ9jI2MD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:41.075Z'
+ }
+ ]
+ },
+ {
+ tester: {
+ username: 'esmeralda-baggins'
},
- minimumAtVersion: {
- id: '3',
- name: '11.6 (20G165)'
+ testPlanReport: {
+ id: '2'
},
- exactAtVersion: null,
- testPlanReport: null
- }
- ]
+ testResults: [
+ {
+ test: {
+ id: 'Nzg5NeyIyIjoiNyJ9zNjZj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:36.666Z'
+ },
+ {
+ test: {
+ id: 'MmY0YeyIyIjoiNyJ9jRkZD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:36.793Z'
+ },
+ {
+ test: {
+ id: 'ZjUwNeyIyIjoiNyJ9mE2ZT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:36.914Z'
+ },
+ {
+ test: {
+ id: 'MDNiMeyIyIjoiNyJ9Dk1MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.031Z'
+ },
+ {
+ test: {
+ id: 'MjRmNeyIyIjoiNyJ92MyMT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.150Z'
+ },
+ {
+ test: {
+ id: 'ZmVlMeyIyIjoiNyJ9mUyYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: null
+ },
+ {
+ test: {
+ id: 'YWFiNeyIyIjoiNyJ9zE2Zj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.384Z'
+ },
+ {
+ test: {
+ id: 'YjZkYeyIyIjoiNyJ9WIxZm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.512Z'
+ },
+ {
+ test: {
+ id: 'ZmIzMeyIyIjoiNyJ9TQ1NW'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.638Z'
+ },
+ {
+ test: {
+ id: 'MmZkNeyIyIjoiNyJ9zIwN2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.760Z'
+ },
+ {
+ test: {
+ id: 'ZmQwOeyIyIjoiNyJ9DEzYz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:37.883Z'
+ },
+ {
+ test: {
+ id: 'MGViNeyIyIjoiNyJ9GQ3MT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.014Z'
+ },
+ {
+ test: {
+ id: 'YTg5MeyIyIjoiNyJ9WEzOT'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.145Z'
+ },
+ {
+ test: {
+ id: 'NTRjMeyIyIjoiNyJ9zQ0OD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.268Z'
+ },
+ {
+ test: {
+ id: 'MjRlZeyIyIjoiNyJ9DcyY2'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.382Z'
+ },
+ {
+ test: {
+ id: 'YWQzNeyIyIjoiNyJ9mE2Nm'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.481Z'
+ },
+ {
+ test: {
+ id: 'OTYxOeyIyIjoiNyJ9TdmYj'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.596Z'
+ },
+ {
+ test: {
+ id: 'MjgzNeyIyIjoiNyJ9TZjNz'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.701Z'
+ },
+ {
+ test: {
+ id: 'NWNiZeyIyIjoiNyJ9jI2MD'
+ },
+ atVersion: {
+ id: '2',
+ name: '2020.4'
+ },
+ browserVersion: {
+ id: '1',
+ name: '99.0.1'
+ },
+ completedAt: '2024-04-25T16:44:38.811Z'
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ isRequired: true,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '3',
+ key: 'safari_macos',
+ name: 'Safari'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ },
+ {
+ isRequired: false,
+ at: {
+ id: '3',
+ key: 'voiceover_macos',
+ name: 'VoiceOver for macOS',
+ atVersions: [
+ {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ }
+ ]
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ },
+ minimumAtVersion: {
+ id: '3',
+ name: '11.6 (20G165)',
+ supportedByAutomation: false,
+ releasedAt: '2020-04-01T04:00:00.000Z'
+ },
+ exactAtVersion: null,
+ testPlanReport: null
+ }
+ ]
};
export default (
- meQuery,
- testPlanReportStatusDialogQuery,
- existingTestPlanReportsQuery
+ meQuery,
+ testPlanReportStatusDialogQuery,
+ existingTestPlanReportsQuery
) => [
- {
- request: {
- query: meQuery
- },
- result: {
- data: {
- me: {
- id: '1',
- username: 'foo-bar',
- roles: ['ADMIN', 'TESTER']
- }
- }
- }
+ {
+ request: {
+ query: meQuery
},
- {
- request: {
- query: testPlanReportStatusDialogQuery,
- variables: { testPlanVersionId: '7' }
- },
- result: {
- data: {
- testPlanVersion: mockedTestPlanVersion
- }
+ result: {
+ data: {
+ me: {
+ id: '1',
+ username: 'foo-bar',
+ roles: ['ADMIN', 'TESTER']
}
+ }
+ }
+ },
+ {
+ request: {
+ query: testPlanReportStatusDialogQuery,
+ variables: { testPlanVersionId: '7' }
},
- {
- request: {
- query: existingTestPlanReportsQuery,
- variables: {
- testPlanVersionId: '7',
- directory: 'combobox-select-only'
+ result: {
+ data: {
+ testPlanVersion: mockedTestPlanVersion
+ }
+ }
+ },
+ {
+ request: {
+ query: existingTestPlanReportsQuery,
+ variables: {
+ testPlanVersionId: '7',
+ directory: 'combobox-select-only'
+ }
+ },
+ result: {
+ data: {
+ existingTestPlanVersion: {
+ id: '7',
+ testPlanReports: [
+ {
+ id: '1',
+ markedFinalAt: '2021-01-01T00:00:00.000Z',
+ isFinal: true,
+ draftTestPlanRuns: {
+ initiatedByAutomation: true
+ },
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '2',
+ key: 'firefox',
+ name: 'Firefox'
+ }
}
+ ],
+ metadata: {
+ exampleUrl: 'https://fakeurl.com/exampleUrl',
+ designPatternUrl: 'https://fakeurl.com/designPattern',
+ testFormatVersion: 1
+ }
},
- result: {
- data: {
- existingTestPlanVersion: {
- id: '7',
- testPlanReports: [
- {
- id: '1',
- markedFinalAt: '2021-01-01T00:00:00.000Z',
- isFinal: true,
- draftTestPlanRuns: {
- initiatedByAutomation: true
- },
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'firefox',
- name: 'Firefox'
- }
- }
- ],
- metadata: {
- exampleUrl: 'https://fakeurl.com/exampleUrl',
- designPatternUrl: 'https://fakeurl.com/designPattern',
- testFormatVersion: 1
- }
- },
- oldTestPlanVersions: []
- }
- }
+ oldTestPlanVersions: []
+ }
}
+ }
];
diff --git a/client/tests/__mocks__/GraphQLMocks/TestQueuePageAdminNotPopulatedMock.js b/client/tests/__mocks__/GraphQLMocks/TestQueuePageAdminNotPopulatedMock.js
index 98ccbfc20..32c8de0cf 100644
--- a/client/tests/__mocks__/GraphQLMocks/TestQueuePageAdminNotPopulatedMock.js
+++ b/client/tests/__mocks__/GraphQLMocks/TestQueuePageAdminNotPopulatedMock.js
@@ -1,45 +1,47 @@
export default testQueuePageQuery => [
- {
- request: {
- query: testQueuePageQuery
+ {
+ request: {
+ query: testQueuePageQuery
+ },
+ result: {
+ data: {
+ me: {
+ __typename: 'User',
+ id: '1',
+ username: 'foo-bar',
+ roles: ['ADMIN', 'TESTER']
},
- result: {
- data: {
- me: {
- id: '1',
- username: 'foo-bar',
- roles: ['ADMIN', 'TESTER'],
- __typename: 'User'
- },
- ats: [],
- browsers: [],
- users: [
- {
- id: '1',
- username: 'foo-bar',
- roles: ['ADMIN', 'TESTER'],
- isBot: false,
- ats: []
- },
- {
- id: '4',
- username: 'bar-foo',
- roles: ['TESTER'],
- isBot: false,
- ats: []
- },
- {
- id: '5',
- username: 'boo-far',
- roles: ['TESTER'],
- isBot: false,
- ats: []
- }
- ],
- testPlanVersions: [],
- testPlanReports: [],
- testPlans: []
- }
- }
+ users: [
+ {
+ __typename: 'User',
+ id: '1',
+ username: 'foo-bar',
+ roles: ['ADMIN', 'TESTER'],
+ isBot: false,
+ ats: []
+ },
+ {
+ __typename: 'User',
+ id: '4',
+ username: 'bar-foo',
+ roles: ['TESTER'],
+ isBot: false,
+ ats: []
+ },
+ {
+ __typename: 'User',
+ id: '5',
+ username: 'boo-far',
+ roles: ['TESTER'],
+ isBot: false,
+ ats: []
+ }
+ ],
+ ats: [],
+ testPlans: [],
+ testPlanVersions: [],
+ testPlanReports: []
+ }
}
+ }
];
diff --git a/client/tests/__mocks__/GraphQLMocks/TestQueuePageAdminPopulatedMock.js b/client/tests/__mocks__/GraphQLMocks/TestQueuePageAdminPopulatedMock.js
deleted file mode 100644
index 7012bd518..000000000
--- a/client/tests/__mocks__/GraphQLMocks/TestQueuePageAdminPopulatedMock.js
+++ /dev/null
@@ -1,379 +0,0 @@
-export default testQueuePageQuery => [
- {
- request: {
- query: testQueuePageQuery
- },
- result: {
- data: {
- me: {
- id: '101',
- username: 'alflennik',
- roles: ['ADMIN', 'TESTER'],
- isBot: false
- },
- ats: [
- {
- id: '1',
- name: 'JAWS',
- key: 'jaws',
- atVersions: [
- {
- id: '1',
- name: '2021.2111.13',
- releasedAt: '2021-11-01T04:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- }
- ],
- candidateBrowsers: [
- {
- id: '2',
- name: 'Chrome'
- }
- ],
- recommendedBrowsers: [
- {
- id: '1',
- name: 'Firefox'
- },
- {
- id: '2',
- name: 'Chrome'
- }
- ]
- },
- {
- id: '2',
- key: 'nvda',
- name: 'NVDA',
- atVersions: [
- {
- id: '5',
- name: '2020.4',
- releasedAt: '2022-01-01T12:00:00.000Z'
- },
- {
- id: '4',
- name: '2020.3',
- releasedAt: '2022-01-01T12:00:00.000Z'
- },
- {
- id: '3',
- name: '2020.2',
- releasedAt: '2022-01-01T12:00:00.000Z'
- },
- {
- id: '2',
- name: '2020.1',
- releasedAt: '2022-01-01T12:00:00.000Z'
- },
- {
- id: '1',
- name: '2019.3',
- releasedAt: '2022-01-01T12:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- }
- ],
- candidateBrowsers: [
- {
- id: '2',
- name: 'Chrome'
- }
- ],
- recommendedBrowsers: [
- {
- id: '1',
- name: 'Firefox'
- },
- {
- id: '2',
- name: 'Chrome'
- }
- ]
- },
- {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS',
- atVersions: [
- {
- id: '3',
- name: '11.6 (20G165)',
- releasedAt: '2019-09-01T04:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- }
- ],
- candidateBrowsers: [
- {
- id: '3',
- name: 'Safari'
- }
- ],
- recommendedBrowsers: [
- {
- id: '3',
- name: 'Safari'
- },
- {
- id: '2',
- name: 'Chrome'
- }
- ]
- }
- ],
- browsers: [
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- }
- ],
- users: [
- {
- id: '1',
- username: 'esmeralda-baggins',
- roles: ['TESTER', 'ADMIN'],
- isBot: false,
- ats: []
- },
- {
- id: '2',
- username: 'tom-proudfeet',
- roles: ['TESTER'],
- isBot: false,
- ats: []
- },
- {
- id: '101',
- username: 'alflennik',
- roles: ['TESTER', 'ADMIN'],
- isBot: false,
- ats: []
- }
- ],
- testPlanVersions: [
- {
- id: '1',
- title: 'Alert Example',
- phase: 'DRAFT',
- gitSha: '97d4bd6c2078849ad4ee01eeeb3667767ca6f992',
- gitMessage:
- 'Create tests for APG design pattern example: Navigation Menu Button (#524)',
- testPlan: {
- directory: 'alert'
- },
- updatedAt: '2022-04-15T19:09:53.000Z'
- },
- {
- id: '2',
- title: 'Banner Landmark',
- phase: 'DRAFT',
- gitSha: '97d4bd6c2078849ad4ee01eeeb3667767ca6f992',
- gitMessage:
- 'Create tests for APG design pattern example: Navigation Menu Button (#524)',
- testPlan: {
- directory: 'banner'
- },
- updatedAt: '2022-04-15T19:09:53.000Z'
- },
- {
- id: '3',
- title: 'Breadcrumb Example',
- phase: 'DRAFT',
- gitSha: '97d4bd6c2078849ad4ee01eeeb3667767ca6f992',
- gitMessage:
- 'Create tests for APG design pattern example: Navigation Menu Button (#524)',
- testPlan: {
- directory: 'breadcrumb'
- },
- updatedAt: '2022-04-15T19:09:53.000Z'
- }
- ],
- testPlanReports: [
- {
- id: '1',
- status: 'DRAFT',
- conflictsLength: 0,
- runnableTestsLength: 17,
- markedFinalAt: null,
- at: { id: '1', name: 'JAWS', key: 'jaws' },
- minimumAtVersion: { id: '1', name: '2024.3321.1' },
- exactAtVersion: null,
- browser: { id: '2', name: 'Chrome', key: 'chrome' },
- testPlanVersion: {
- id: '1',
- title: 'Checkbox Example (Two State)',
- phase: 'DRAFT',
- gitSha: 'b7078039f789c125e269cb8f8632f57a03d4c50b',
- gitMessage: 'The message for this SHA',
- testPlan: { directory: 'checkbox' },
- versionString: 'V21.11.30'
- },
- draftTestPlanRuns: [
- {
- id: '1',
- tester: {
- id: '1',
- username: 'esmeralda-baggins',
- isBot: false
- },
- testResultsLength: 0,
- initiatedByAutomation: false
- }
- ]
- },
- {
- id: '2',
- status: 'DRAFT',
- conflictsLength: 0,
- runnableTestsLength: 17,
- markedFinalAt: null,
- at: {
- id: '3',
- name: 'VoiceOver for macOS',
- key: 'voiceover_macos'
- },
- minimumAtVersion: { id: '3', name: '14.5' },
- exactAtVersion: null,
- browser: {
- id: '3',
- name: 'Safari',
- key: 'safari_macos'
- },
- testPlanVersion: {
- id: '1',
- title: 'Checkbox Example (Two State)',
- phase: 'DRAFT',
- gitSha: 'b7078039f789c125e269cb8f8632f57a03d4c50b',
- gitMessage: 'The message for this SHA',
- testPlan: { directory: 'checkbox' },
- versionString: 'V21.11.30'
- },
- draftTestPlanRuns: [
- {
- id: '1',
- tester: {
- id: '1',
- username: 'esmeralda-baggins',
- isBot: false
- },
- testResultsLength: 0,
- initiatedByAutomation: false
- }
- ]
- },
- {
- id: '3',
- status: 'DRAFT',
- conflictsLength: 3,
- runnableTestsLength: 17,
- markedFinalAt: null,
- at: { id: '2', name: 'NVDA', key: 'nvda' },
- minimumAtVersion: null,
- exactAtVersion: { id: '2', name: '2024.2' },
- browser: { id: '1', name: 'Firefox', key: 'firefox' },
- testPlanVersion: {
- id: '1',
- title: 'Checkbox Example (Two State)',
- phase: 'DRAFT',
- gitSha: 'b7078039f789c125e269cb8f8632f57a03d4c50b',
- gitMessage: 'The message for this SHA',
- testPlan: { directory: 'checkbox' },
- versionString: 'V21.11.30'
- },
- draftTestPlanRuns: [
- {
- id: '3',
- tester: {
- id: '2',
- username: 'tom-proudfeet',
- isBot: false
- },
- testResultsLength: 3,
- initiatedByAutomation: false
- },
- {
- id: '101',
- tester: {
- id: '101',
- username: 'alflennik',
- isBot: false
- },
- testResultsLength: 1,
- initiatedByAutomation: false
- },
- {
- id: '2',
- tester: {
- id: '1',
- username: 'esmeralda-baggins',
- isBot: false
- },
- testResultsLength: 3,
- initiatedByAutomation: false
- }
- ]
- }
- ],
- testPlans: []
- }
- }
- }
-];
diff --git a/client/tests/__mocks__/GraphQLMocks/TestQueuePageBaseMock.js b/client/tests/__mocks__/GraphQLMocks/TestQueuePageBaseMock.js
index 13afebebd..a7e800ce6 100644
--- a/client/tests/__mocks__/GraphQLMocks/TestQueuePageBaseMock.js
+++ b/client/tests/__mocks__/GraphQLMocks/TestQueuePageBaseMock.js
@@ -1,117 +1,114 @@
export default (testPlanReportAtBrowserQuery, existingTestPlanReportsQuery) => [
- {
- request: {
- query: testPlanReportAtBrowserQuery,
- variables: {
- testPlanReportId: '3'
- }
- },
- result: {
- data: {
- testPlanReport: {
- id: '3',
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- }
- }
- }
+ {
+ request: {
+ query: testPlanReportAtBrowserQuery,
+ variables: {
+ testPlanReportId: '3'
+ }
+ },
+ result: {
+ data: {
+ testPlanReport: {
+ id: '3',
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ }
}
+ }
+ }
+ },
+ {
+ request: {
+ query: testPlanReportAtBrowserQuery,
+ variables: {
+ testPlanReportId: '2'
+ }
},
- {
- request: {
- query: testPlanReportAtBrowserQuery,
- variables: {
- testPlanReportId: '2'
- }
- },
- result: {
- data: {
- testPlanReport: {
- id: '2',
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- }
- }
- }
+ result: {
+ data: {
+ testPlanReport: {
+ id: '2',
+ at: {
+ id: '2',
+ key: 'nvda',
+ name: 'NVDA'
+ },
+ browser: {
+ id: '1',
+ key: 'firefox',
+ name: 'Firefox'
+ }
}
+ }
+ }
+ },
+ {
+ request: {
+ query: testPlanReportAtBrowserQuery,
+ variables: {
+ testPlanReportId: '1'
+ }
},
- {
- request: {
- query: testPlanReportAtBrowserQuery,
- variables: {
- testPlanReportId: '1'
- }
- },
- result: {
- data: {
- testPlanReport: {
- id: '1',
- at: {
- id: '1',
- key: 'jaws',
- name: 'JAWS'
- },
- browser: {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- }
- }
- }
+ result: {
+ data: {
+ testPlanReport: {
+ id: '1',
+ at: {
+ id: '1',
+ key: 'jaws',
+ name: 'JAWS'
+ },
+ browser: {
+ id: '2',
+ key: 'chrome',
+ name: 'Chrome'
+ }
}
+ }
+ }
+ },
+ {
+ request: {
+ query: existingTestPlanReportsQuery,
+ variables: {
+ testPlanVersionId: '1',
+ directory: 'alert'
+ }
},
- {
- request: {
- query: existingTestPlanReportsQuery,
- variables: {
- testPlanVersionId: '1',
- directory: 'alert'
+ result: {
+ data: {
+ existingTestPlanVersion: {
+ id: '1',
+ testPlanReports: [
+ {
+ id: '7',
+ markedFinalAt: null,
+ isFinal: false,
+ draftTestPlanRuns: [],
+ at: {
+ id: '3'
+ },
+ browser: {
+ id: '1'
+ }
}
+ ],
+ metadata: {
+ exampleUrl:
+ 'https://w3c.github.io/aria-practices/examples/alert/alert.html',
+ designPatternUrl: 'https://w3c.github.io/aria-practices/#alert',
+ testFormatVersion: 1
+ }
},
- result: {
- data: {
- existingTestPlanVersion: {
- id: '1',
- testPlanReports: [
- {
- id: '1',
- markedFinalAt: '2021-01-01T00:00:00.000Z',
- isFinal: true,
- draftTestPlanRuns: {
- initiatedByAutomation: true
- },
- at: {
- id: '1',
- key: 'jaws'
- },
- browser: {
- id: '2',
- key: 'chrome'
- }
- }
- ],
- metadata: {
- exampleUrl: 'https://fakeurl.com/exampleUrl',
- designPatternUrl: 'https://fakeurl.com/designPattern',
- testFormatVersion: 1
- }
- },
- oldTestPlanVersions: []
- }
- }
+ oldTestPlanVersions: []
+ }
}
+ }
];
diff --git a/client/tests/__mocks__/GraphQLMocks/TestQueuePageTesterNotPopulatedMock.js b/client/tests/__mocks__/GraphQLMocks/TestQueuePageTesterNotPopulatedMock.js
index 8b600c669..186f6ac4f 100644
--- a/client/tests/__mocks__/GraphQLMocks/TestQueuePageTesterNotPopulatedMock.js
+++ b/client/tests/__mocks__/GraphQLMocks/TestQueuePageTesterNotPopulatedMock.js
@@ -1,48 +1,47 @@
export default testQueuePageQuery => [
- {
- request: {
- query: testQueuePageQuery
+ {
+ request: {
+ query: testQueuePageQuery
+ },
+ result: {
+ data: {
+ me: {
+ __typename: 'User',
+ id: '4',
+ username: 'bar-foo',
+ roles: ['TESTER']
},
- result: {
- data: {
- me: {
- id: '4',
- username: 'bar-foo',
- roles: ['TESTER'],
- __typename: 'User'
- },
- ats: [],
- browsers: [],
- users: [
- {
- id: '1',
- username: 'foo-bar',
- roles: ['ADMIN', 'TESTER'],
- isBot: false,
- ats: [],
- __typename: 'User'
- },
- {
- id: '4',
- username: 'bar-foo',
- roles: ['TESTER'],
- isBot: false,
- ats: [],
- __typename: 'User'
- },
- {
- id: '5',
- username: 'boo-far',
- roles: ['TESTER'],
- isBot: false,
- ats: [],
- __typename: 'User'
- }
- ],
- testPlanVersions: [],
- testPlanReports: [],
- testPlans: []
- }
- }
+ users: [
+ {
+ __typename: 'User',
+ id: '1',
+ username: 'foo-bar',
+ roles: ['ADMIN', 'TESTER'],
+ isBot: false,
+ ats: []
+ },
+ {
+ __typename: 'User',
+ id: '4',
+ username: 'bar-foo',
+ roles: ['TESTER'],
+ isBot: false,
+ ats: []
+ },
+ {
+ __typename: 'User',
+ id: '5',
+ username: 'boo-far',
+ roles: ['TESTER'],
+ isBot: false,
+ ats: []
+ }
+ ],
+ ats: [],
+ testPlans: [],
+ testPlanVersions: [],
+ testPlanReports: []
+ }
}
+ }
];
diff --git a/client/tests/__mocks__/GraphQLMocks/TestQueuePageTesterPopulatedMock.js b/client/tests/__mocks__/GraphQLMocks/TestQueuePageTesterPopulatedMock.js
deleted file mode 100644
index f4be09013..000000000
--- a/client/tests/__mocks__/GraphQLMocks/TestQueuePageTesterPopulatedMock.js
+++ /dev/null
@@ -1,380 +0,0 @@
-export default testQueuePageQuery => [
- {
- request: {
- query: testQueuePageQuery
- },
- result: {
- data: {
- me: {
- id: '4',
- username: 'bar-foo',
- roles: ['TESTER'],
- __typename: 'User'
- },
- ats: [
- {
- id: '1',
- key: 'jaws',
- name: 'JAWS',
- atVersions: [
- {
- id: '1',
- name: '2021.2111.13',
- releasedAt: '2021-11-01T04:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- }
- ],
- candidateBrowsers: [
- {
- id: '2',
- name: 'Chrome'
- }
- ],
- recommendedBrowsers: [
- {
- id: '1',
- name: 'Firefox'
- },
- {
- id: '2',
- name: 'Chrome'
- }
- ]
- },
- {
- id: '2',
- key: 'nvda',
- name: 'NVDA',
- atVersions: [
- {
- id: '5',
- name: '2020.4',
- releasedAt: '2022-01-01T12:00:00.000Z'
- },
- {
- id: '4',
- name: '2020.3',
- releasedAt: '2022-01-01T12:00:00.000Z'
- },
- {
- id: '3',
- name: '2020.2',
- releasedAt: '2022-01-01T12:00:00.000Z'
- },
- {
- id: '2',
- name: '2020.1',
- releasedAt: '2022-01-01T12:00:00.000Z'
- },
- {
- id: '1',
- name: '2019.3',
- releasedAt: '2022-01-01T12:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- }
- ],
- candidateBrowsers: [
- {
- id: '2',
- name: 'Chrome'
- }
- ],
- recommendedBrowsers: [
- {
- id: '1',
- name: 'Firefox'
- },
- {
- id: '2',
- name: 'Chrome'
- }
- ]
- },
- {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS',
- atVersions: [
- {
- id: '3',
- name: '11.6 (20G165)',
- releasedAt: '2019-09-01T04:00:00.000Z'
- }
- ],
- browsers: [
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- }
- ],
- candidateBrowsers: [
- {
- id: '3',
- name: 'Safari'
- }
- ],
- recommendedBrowsers: [
- {
- id: '3',
- name: 'Safari'
- },
- {
- id: '2',
- name: 'Chrome'
- }
- ]
- }
- ],
- browsers: [
- {
- id: '2',
- key: 'chrome',
- name: 'Chrome'
- },
- {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- {
- id: '3',
- key: 'safari_macos',
- name: 'Safari'
- }
- ],
- users: [
- {
- id: '1',
- username: 'foo-bar',
- roles: ['ADMIN', 'TESTER'],
- isBot: false,
- ats: []
- },
- {
- id: '4',
- username: 'bar-foo',
- roles: ['TESTER'],
- isBot: false,
- ats: []
- },
- {
- id: '5',
- username: 'boo-far',
- roles: ['TESTER'],
- isBot: false,
- ats: []
- }
- ],
- testPlanVersions: [
- {
- id: '1',
- title: 'Alert Example',
- phase: 'DRAFT',
- gitSha: '97d4bd6c2078849ad4ee01eeeb3667767ca6f992',
- gitMessage:
- 'Create tests for APG design pattern example: Navigation Menu Button (#524)',
- testPlan: {
- directory: 'alert'
- },
- updatedAt: '2022-04-15T19:09:53.000Z'
- },
- {
- id: '2',
- title: 'Banner Landmark',
- phase: 'DRAFT',
- gitSha: '97d4bd6c2078849ad4ee01eeeb3667767ca6f992',
- gitMessage:
- 'Create tests for APG design pattern example: Navigation Menu Button (#524)',
- testPlan: {
- directory: 'banner'
- },
- updatedAt: '2022-04-15T19:09:53.000Z'
- },
- {
- id: '3',
- title: 'Breadcrumb Example',
- phase: 'DRAFT',
- gitSha: '97d4bd6c2078849ad4ee01eeeb3667767ca6f992',
- gitMessage:
- 'Create tests for APG design pattern example: Navigation Menu Button (#524)',
- testPlan: {
- directory: 'breadcrumb'
- },
- updatedAt: '2022-04-15T19:09:53.000Z'
- }
- ],
- testPlanReports: [
- {
- id: '10',
- status: 'DRAFT',
- conflictsLength: 0,
- runnableTestsLength: 17,
- markedFinalAt: null,
- at: {
- id: '2',
- key: 'nvda',
- name: 'NVDA'
- },
- minimumAtVersion: { id: '1', name: '2024.3321.1' },
- exactAtVersion: null,
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- testPlanVersion: {
- id: '65',
- title: 'Checkbox Example (Two State)',
- phase: 'DRAFT',
- gitSha: 'aea64f84b8fa8b21e94f5d9afd7035570bc1bed3',
- gitMessage: 'The message for this SHA',
- testPlan: {
- directory: 'checkbox'
- },
- versionString: 'V21-11-30'
- },
- draftTestPlanRuns: [
- {
- id: '18',
- tester: {
- id: '1',
- username: 'foo-bar',
- isBot: false
- },
- testResultsLength: 0,
- initiatedByAutomation: false
- },
- {
- id: '19',
- tester: {
- id: '4',
- username: 'bar-foo',
- isBot: false
- },
- testResultsLength: 0,
- initiatedByAutomation: false
- }
- ]
- },
- {
- id: '11',
- status: 'DRAFT',
- conflictsLength: 0,
- runnableTestsLength: 17,
- markedFinalAt: null,
- at: {
- id: '2',
- key: 'jaws',
- name: 'JAWS'
- },
- minimumAtVersion: { id: '3', name: '14.5' },
- exactAtVersion: null,
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- testPlanVersion: {
- id: '65',
- title: 'Checkbox Example (Two State)',
- phase: 'DRAFT',
- gitSha: 'aea64f84b8fa8b21e94f5d9afd7035570bc1bed3',
- gitMessage: 'The message for this SHA',
- testPlan: {
- directory: 'checkbox'
- },
- versionString: 'V21-11-30'
- },
- draftTestPlanRuns: [
- {
- id: '20',
- tester: {
- id: '5',
- username: 'boo-far',
- isBot: false
- },
- testResultsLength: 0,
- initiatedByAutomation: false
- }
- ]
- },
- {
- id: '12',
- status: 'DRAFT',
- conflictsLength: 0,
- runnableTestsLength: 15,
- markedFinalAt: null,
- at: {
- id: '3',
- key: 'voiceover_macos',
- name: 'VoiceOver for macOS'
- },
- minimumAtVersion: null,
- exactAtVersion: { id: '2', name: '2024.2' },
- browser: {
- id: '1',
- key: 'firefox',
- name: 'Firefox'
- },
- testPlanVersion: {
- id: '74',
- title: 'Editor Menubar Example',
- phase: 'DRAFT',
- gitSha: 'aea64f84b8fa8b21e94f5d9afd7035570bc1bed3',
- gitMessage: 'The message for this SHA',
- testPlan: {
- directory: 'menubar-editor'
- },
- versionString: 'V21-11-30'
- },
- draftTestPlanRuns: []
- }
- ],
- testPlans: []
- }
- }
- }
-];
diff --git a/client/tests/__mocks__/GraphQLMocks/index.js b/client/tests/__mocks__/GraphQLMocks/index.js
index 395e83e9b..f2e5142a4 100644
--- a/client/tests/__mocks__/GraphQLMocks/index.js
+++ b/client/tests/__mocks__/GraphQLMocks/index.js
@@ -1,45 +1,38 @@
import { TEST_QUEUE_PAGE_QUERY } from '@components/TestQueue/queries';
+import { TEST_PLAN_REPORT_AT_BROWSER_QUERY } from '@components/common/AssignTesterDropdown/queries';
import { DATA_MANAGEMENT_PAGE_QUERY } from '@components/DataManagement/queries';
import { TEST_PLAN_REPORT_STATUS_DIALOG_QUERY } from '@components/TestPlanReportStatusDialog/queries';
-import { TEST_PLAN_REPORT_AT_BROWSER_QUERY } from '@components/TestQueue/queries';
import { EXISTING_TEST_PLAN_REPORTS } from '@components/AddTestToQueueWithConfirmation/queries';
import { ME_QUERY } from '@components/App/queries';
import TestQueuePageAdminNotPopulatedMock from './TestQueuePageAdminNotPopulatedMock';
-import TestQueuePageAdminPopulatedMock from './TestQueuePageAdminPopulatedMock';
import TestQueuePageTesterNotPopulatedMock from './TestQueuePageTesterNotPopulatedMock';
-import TestQueuePageTesterPopulatedMock from './TestQueuePageTesterPopulatedMock';
+
import DataManagementPagePopulatedMock from './DataManagementPagePopulatedMock';
import TestPlanReportStatusDialogMock from './TestPlanReportStatusDialogMock';
import TestQueuePageBaseMock from './TestQueuePageBaseMock';
export const TEST_QUEUE_PAGE_ADMIN_NOT_POPULATED_MOCK_DATA =
- TestQueuePageAdminNotPopulatedMock(TEST_QUEUE_PAGE_QUERY);
-
-export const TEST_QUEUE_PAGE_ADMIN_POPULATED_MOCK_DATA =
- TestQueuePageAdminPopulatedMock(TEST_QUEUE_PAGE_QUERY);
+ TestQueuePageAdminNotPopulatedMock(TEST_QUEUE_PAGE_QUERY);
export const TEST_QUEUE_PAGE_TESTER_NOT_POPULATED_MOCK_DATA =
- TestQueuePageTesterNotPopulatedMock(TEST_QUEUE_PAGE_QUERY);
-
-export const TEST_QUEUE_PAGE_TESTER_POPULATED_MOCK_DATA =
- TestQueuePageTesterPopulatedMock(TEST_QUEUE_PAGE_QUERY);
+ TestQueuePageTesterNotPopulatedMock(TEST_QUEUE_PAGE_QUERY);
export const TEST_QUEUE_PAGE_BASE_MOCK_DATA = TestQueuePageBaseMock(
- TEST_PLAN_REPORT_AT_BROWSER_QUERY,
- EXISTING_TEST_PLAN_REPORTS
+ TEST_PLAN_REPORT_AT_BROWSER_QUERY,
+ EXISTING_TEST_PLAN_REPORTS
);
export const DATA_MANAGEMENT_PAGE_POPULATED_MOCK_DATA =
- DataManagementPagePopulatedMock(
- ME_QUERY,
- DATA_MANAGEMENT_PAGE_QUERY,
- TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
- );
+ DataManagementPagePopulatedMock(
+ ME_QUERY,
+ DATA_MANAGEMENT_PAGE_QUERY,
+ TEST_PLAN_REPORT_STATUS_DIALOG_QUERY
+ );
export const TEST_PLAN_REPORT_STATUS_DIALOG_MOCK_DATA =
- TestPlanReportStatusDialogMock(
- ME_QUERY,
- TEST_PLAN_REPORT_STATUS_DIALOG_QUERY,
- EXISTING_TEST_PLAN_REPORTS
- );
+ TestPlanReportStatusDialogMock(
+ ME_QUERY,
+ TEST_PLAN_REPORT_STATUS_DIALOG_QUERY,
+ EXISTING_TEST_PLAN_REPORTS
+ );
diff --git a/client/tests/calculatePercentComplete.test.js b/client/tests/calculatePercentComplete.test.js
index 03cda0adf..a38f12d7b 100644
--- a/client/tests/calculatePercentComplete.test.js
+++ b/client/tests/calculatePercentComplete.test.js
@@ -1,68 +1,68 @@
import { calculatePercentComplete } from '../utils/calculatePercentComplete';
describe('calculatePercentComplete', () => {
- const testResult = (id, completedAt = null) => ({ id, completedAt });
+ const testResult = (id, completedAt = null) => ({ id, completedAt });
- test('returns 0 when metrics or draftTestPlanRuns is not defined', () => {
- expect(calculatePercentComplete({})).toBe(0);
- expect(calculatePercentComplete({ metrics: {} })).toBe(0);
- expect(
- calculatePercentComplete({
- draftTestPlanRuns: []
- })
- ).toBe(0);
- });
+ test('returns 0 when metrics or draftTestPlanRuns is not defined', () => {
+ expect(calculatePercentComplete({})).toBe(0);
+ expect(calculatePercentComplete({ metrics: {} })).toBe(0);
+ expect(
+ calculatePercentComplete({
+ draftTestPlanRuns: []
+ })
+ ).toBe(0);
+ });
- test('returns 0 when draftTestPlanRuns is empty', () => {
- expect(
- calculatePercentComplete({
- metrics: { testsCount: 5 },
- draftTestPlanRuns: []
- })
- ).toBe(0);
- });
+ test('returns 0 when draftTestPlanRuns is empty', () => {
+ expect(
+ calculatePercentComplete({
+ metrics: { testsCount: 5 },
+ draftTestPlanRuns: []
+ })
+ ).toBe(0);
+ });
- test('returns 0 and not Infinity when total tests possible is 0', () => {
- const metrics = { testsCount: 0 };
- const t1 = testResult(1);
- const t2 = testResult(2);
- const t3 = testResult(3);
- const t4 = testResult(4);
- const t5 = testResult(5);
+ test('returns 0 and not Infinity when total tests possible is 0', () => {
+ const metrics = { testsCount: 0 };
+ const t1 = testResult(1);
+ const t2 = testResult(2);
+ const t3 = testResult(3);
+ const t4 = testResult(4);
+ const t5 = testResult(5);
- const draftTestPlanRuns = [
- { testResults: [t1, t2, t3] },
- { testResults: [t1, t2, t3, t4, t5] }
- ];
+ const draftTestPlanRuns = [
+ { testResults: [t1, t2, t3] },
+ { testResults: [t1, t2, t3, t4, t5] }
+ ];
- expect(
- calculatePercentComplete({
- metrics,
- draftTestPlanRuns
- })
- ).toBe(0);
- });
+ expect(
+ calculatePercentComplete({
+ metrics,
+ draftTestPlanRuns
+ })
+ ).toBe(0);
+ });
- test('calculates and returns the correct percentage when draftTestPlanRuns has testResults', () => {
- const metrics = { testsCount: 5 };
- const date = new Date();
- const t1 = testResult(1, date);
- const t2 = testResult(2, date);
- const t3 = testResult(3, date);
+ test('calculates and returns the correct percentage when draftTestPlanRuns has testResults', () => {
+ const metrics = { testsCount: 5 };
+ const date = new Date();
+ const t1 = testResult(1, date);
+ const t2 = testResult(2, date);
+ const t3 = testResult(3, date);
- const draftTestPlanRuns = [
- { testResults: [t1, t2] },
- { testResults: [t1, t2, t3] }
- ];
+ const draftTestPlanRuns = [
+ { testResults: [t1, t2] },
+ { testResults: [t1, t2, t3] }
+ ];
- // Output should follow this formula:
- // (NUMBER_COMPLETED_TESTS_BY_ALL_TESTERS / (NUMBER_ASSIGNED_TESTERS * NUMBER_TESTS_IN_PLAN)) * 100
- // (5 / (2 * 5)) * 100 = 50
- expect(
- calculatePercentComplete({
- metrics,
- draftTestPlanRuns
- })
- ).toBe(50);
- });
+ // Output should follow this formula:
+ // (NUMBER_COMPLETED_TESTS_BY_ALL_TESTERS / (NUMBER_ASSIGNED_TESTERS * NUMBER_TESTS_IN_PLAN)) * 100
+ // (5 / (2 * 5)) * 100 = 50
+ expect(
+ calculatePercentComplete({
+ metrics,
+ draftTestPlanRuns
+ })
+ ).toBe(50);
+ });
});
diff --git a/client/tests/e2e/AtVersions.e2e.test.js b/client/tests/e2e/AtVersions.e2e.test.js
new file mode 100644
index 000000000..518a2f217
--- /dev/null
+++ b/client/tests/e2e/AtVersions.e2e.test.js
@@ -0,0 +1,47 @@
+import getPage from '../util/getPage';
+
+describe('AT Version UI', () => {
+ test('should add, edit, then remove an AtVersion', async () => {
+ /* prettier-ignore */
+ await getPage({ role: 'admin', url: '/test-queue' }, async page => {
+ const openTrayIfClosed = async () => {
+ await page.waitForSelector('button ::-p-text(Manage Assistive Technology Versions)');
+ const isTrayClosed = !!(await page.$('::-p-text(Select an assistive technology and manage its versions)'));
+ if (isTrayClosed) {
+ await page.click('button ::-p-text(Manage Assistive Technology Versions)');
+ await page.waitForSelector('::-p-text(Select an assistive technology and manage its versions)');
+ }
+ };
+ await openTrayIfClosed();
+ await page.click('button ::-p-text(Add a New Version)');
+ await page.waitForSelector('.modal-title ::-p-text(Add a New Version for JAWS)');
+ await page.waitForSelector('.modal-body .form-group:nth-child(1) ::-p-text(Version Number)');
+ await page.waitForSelector('.modal-body .form-group:nth-child(2) ::-p-text(Approximate date of availability)');
+ await page.type('.modal-body .form-group:nth-child(1) input', '99.0.1');
+ await page.type('.modal-body .form-group:nth-child(2) input', '01-01-2000');
+ await page.click('.modal-footer button ::-p-text(Add Version)');
+ await page.waitForNetworkIdle({ idleTime: 5000 });
+ await page.click('.modal-footer button ::-p-text(Ok)');
+ await page.waitForSelector('.at-versions-container option:nth-child(2) ::-p-text(99.0.1)');
+ const optionValue = await page.$eval('.at-versions-container option:nth-child(2)', option => option.value);
+ await page.select('.at-versions-container select', optionValue);
+ await page.click('.at-versions-container button ::-p-text(Edit)');
+ const input = await page.waitForSelector('.modal-body .form-group:nth-child(1) input');
+ for (let i = 0; i < 6; i += 1) {
+ await input.press('Backspace');
+ }
+ await page.type('.modal-body .form-group:nth-child(1) input', '99.0.99');
+ await page.click('.modal-footer button ::-p-text(Save)');
+ await page.waitForNetworkIdle({ idleTime: 5000 });
+ await page.click('.modal-footer button ::-p-text(Ok)');
+ await page.waitForSelector('.at-versions-container option ::-p-text(99.0.99)');
+ await page.select('.at-versions-container select', optionValue);
+ await page.click('.at-versions-container button ::-p-text(Remove)');
+ await page.waitForSelector('.modal-title ::-p-text(Remove JAWS Version 99.0.99)');
+ await page.click('.modal-footer button ::-p-text(Remove)');
+ await page.waitForNetworkIdle();
+ const option = await page.$('.at-versions-container option ::-p-text(99.0.99)');
+ expect(option).toBeNull();
+ });
+ });
+});
diff --git a/client/tests/e2e/TestQueue.e2e.test.js b/client/tests/e2e/TestQueue.e2e.test.js
new file mode 100644
index 000000000..f0dc1b409
--- /dev/null
+++ b/client/tests/e2e/TestQueue.e2e.test.js
@@ -0,0 +1,495 @@
+import getPage from '../util/getPage';
+
+const text = async (page, selector, { wait = true } = {}) => {
+ if (wait) await page.waitForSelector(selector);
+ return page.$eval(selector, el => el.innerText);
+};
+
+const display = async (page, selector, { wait = true } = {}) => {
+ if (wait) await page.waitForSelector(selector);
+ return page.$eval(selector, el => {
+ const styles = window.getComputedStyle(el);
+ return styles.getPropertyValue('display');
+ });
+};
+
+describe('Test Queue common traits', () => {
+ it('renders page h1', async () => {
+ await getPage({ role: false, url: '/test-queue' }, async page => {
+ const h1Element = await text(page, 'h1');
+ expect(h1Element).toBe('Test Queue');
+ });
+ });
+});
+
+describe('Test Queue admin traits when reports exist', () => {
+ it('renders page h1', async () => {
+ await getPage({ role: 'admin', url: '/test-queue' }, async page => {
+ const h1Element = await text(page, 'h1');
+ expect(h1Element).toBe('Test Queue');
+ });
+ });
+
+ it('renders page with instructions', async () => {
+ await getPage({ role: 'admin', url: '/test-queue' }, async page => {
+ const instructionsSelector = '[data-testid="test-queue-instructions"]';
+ const instructionsText = await text(page, instructionsSelector);
+
+ expect(instructionsText).toBe(
+ 'Manage the test plans, assign yourself a test plan or start executing one that is already assigned to you.'
+ );
+ });
+ });
+
+ it('renders page with known pattern sections', async () => {
+ await getPage({ role: 'admin', url: '/test-queue' }, async page => {
+ const alertSectionHeaderSelector = 'h2 ::-p-text(Alert Example)';
+ const alertSectionContainerSelector = 'div#disclosure-container-alert-0';
+ const modalDialogSectionHeaderSelector =
+ 'h2 ::-p-text(Modal Dialog Example)';
+ const modalDialogSectionContainerSelector =
+ 'div#disclosure-container-modal-dialog-0';
+
+ const alertSectionTitle = await text(page, alertSectionHeaderSelector);
+ const alertSectionDisplay = await display(
+ page,
+ alertSectionContainerSelector
+ );
+ const modalDialogSectionTitle = await text(
+ page,
+ modalDialogSectionHeaderSelector
+ );
+ const modalDialogSectionDisplay = await display(
+ page,
+ modalDialogSectionContainerSelector
+ );
+
+ expect(alertSectionTitle).toBe('Alert Example');
+ expect(alertSectionDisplay).toBe('none');
+ expect(modalDialogSectionTitle).toBe('Modal Dialog Example');
+ expect(modalDialogSectionDisplay).toBe('none');
+ });
+ });
+
+ it("renders page and open pattern section's table", async () => {
+ await getPage({ role: 'admin', url: '/test-queue' }, async page => {
+ const modalDialogSectionContainerSelector =
+ 'div#disclosure-container-modal-dialog-0';
+ const modalDialogSectionButtonSelector =
+ 'button#disclosure-btn-modal-dialog-0';
+ const modalDialogTableSelector =
+ 'table[aria-label="Reports for Modal Dialog Example V24.06.07 in draft phase"]';
+
+ await page.waitForSelector(modalDialogSectionButtonSelector);
+
+ const modalDialogSectionButton = await text(
+ page,
+ modalDialogSectionContainerSelector
+ );
+ const preClickModalDialogSectionDisplay = await display(
+ page,
+ modalDialogSectionContainerSelector
+ );
+
+ // Expand Modal Dialog's V24.06.07 section
+ await page.click(modalDialogSectionButtonSelector);
+ const postClickModalDialogSectionDisplay = await display(
+ page,
+ modalDialogSectionContainerSelector
+ );
+
+ // Wait for the table to render
+ await page.waitForSelector(modalDialogTableSelector);
+
+ // Check if the table contains expected column data
+ const validTable = await page.$eval(modalDialogTableSelector, el => {
+ const sanitizedText = text =>
+ text
+ .replaceAll(String.fromCharCode(160), ' ') // remove being included
+ .trim();
+
+ // Check if any cell in the table contains expected text
+ const cells = Array.from(el.querySelectorAll('td'));
+
+ // Assistive Technology Column
+ const atColumn = cells[0];
+ const atColumnText = sanitizedText(atColumn.innerText);
+ const atColumnCondition = atColumnText.includes('NVDA 2020.4 or later');
+
+ // Browser Column
+ const browserColumn = cells[1];
+ const browserColumnText = sanitizedText(browserColumn.innerText);
+ const browserColumnCondition =
+ browserColumnText.includes('Chrome Any version');
+
+ // Testers Column
+ const testersColumn = cells[2];
+ const testersColumnText = sanitizedText(testersColumn.innerText);
+ const testersColumnCondition =
+ testersColumnText.includes('Assign Testers') && // sr-only label applied to the assign testers dropdown component
+ testersColumnText.includes('Assign Yourself') &&
+ testersColumnText.includes('esmeralda-baggins') &&
+ testersColumnText.includes('tests complete');
+
+ // Status Column
+ const statusColumn = cells[3];
+ const statusColumnText = sanitizedText(statusColumn.innerText);
+ const statusColumnCondition = statusColumnText.includes(
+ '100% complete by esmeralda-baggins with 0 conflicts'
+ );
+
+ // Actions Column
+ const actionsColumn = cells[4];
+ const actionsColumnText = sanitizedText(actionsColumn.innerText);
+ const actionsColumnCondition =
+ actionsColumnText.includes('Start Testing') &&
+ actionsColumnText.includes('Open run as...') &&
+ actionsColumnText.includes('Mark as Final') &&
+ actionsColumnText.includes('Mark as Final');
+
+ return (
+ atColumnCondition &&
+ browserColumnCondition &&
+ testersColumnCondition &&
+ statusColumnCondition &&
+ actionsColumnCondition
+ );
+ });
+
+ expect(modalDialogSectionButton.includes('V24.06.07')).toBe(true);
+ expect(preClickModalDialogSectionDisplay).toBe('none');
+ expect(postClickModalDialogSectionDisplay).toBe('block');
+ expect(validTable).toBe(true);
+ });
+ });
+
+ it("renders page, opens pattern section's table and assigns bot and shows context related action", async () => {
+ await getPage({ role: 'admin', url: '/test-queue' }, async page => {
+ const modalDialogSectionButtonSelector =
+ 'button#disclosure-btn-modal-dialog-0';
+ const modalDialogTableSelector =
+ 'table[aria-label="Reports for Modal Dialog Example V24.06.07 in draft phase"]';
+
+ await page.waitForSelector(modalDialogSectionButtonSelector);
+
+ // Expand Modal Dialog's V24.06.07 section
+ await page.click(modalDialogSectionButtonSelector);
+
+ // Wait for the table to render
+ await page.waitForSelector(modalDialogTableSelector);
+
+ const assignTestersDropdownButton = await page.evaluateHandle(() => {
+ const modalDialogTableSelector =
+ 'table[aria-label="Reports for Modal Dialog Example V24.06.07 in draft phase"]';
+ const modalDialogTable = document.querySelector(
+ modalDialogTableSelector
+ );
+
+ // Check if any cell in the table contains expected text
+ const cells = Array.from(modalDialogTable.querySelectorAll('td'));
+
+ // Testers Column
+ const testersColumn = cells[2];
+ return testersColumn.querySelector('div.dropdown button');
+ });
+
+ await assignTestersDropdownButton.click();
+
+ // Will be first option on page after clicking the dropdown button
+ const assignTestersMenuSelector = 'div [role="menu"]';
+ await page.waitForSelector(assignTestersMenuSelector);
+
+ const assignBotOptionButton = await page.evaluateHandle(() => {
+ const assignTestersMenuSelector = 'div [role="menu"]';
+ const assignTestersMenu = document.querySelector(
+ assignTestersMenuSelector
+ );
+ const assignTesterOptions = Array.from(
+ assignTestersMenu.querySelectorAll('[role="menuitemcheckbox"]')
+ );
+
+ return assignTesterOptions.find(option =>
+ option.innerText.includes('Bot')
+ );
+ });
+
+ // Assign the bot user found in the assign testers list
+ await assignBotOptionButton.click();
+ await page.waitForSelector('::-p-text(Manage NVDA Bot Run)');
+
+ // Check if the table contains expected column data
+ const validTable = await page.$eval(modalDialogTableSelector, el => {
+ const sanitizedText = text =>
+ text
+ .replaceAll(String.fromCharCode(160), ' ') // remove being included
+ .trim();
+
+ // Check if any cell in the table contains expected text
+ const cells = Array.from(el.querySelectorAll('td'));
+
+ // Actions Column
+ const actionsColumn = cells[4];
+ const actionsColumnText = sanitizedText(actionsColumn.innerText);
+ return (
+ actionsColumnText.includes('Start Testing') &&
+ actionsColumnText.includes('Open run as...') &&
+ actionsColumnText.includes('Mark as Final') &&
+ actionsColumnText.includes('Mark as Final') &&
+ actionsColumnText.includes('Manage NVDA Bot Run')
+ );
+ });
+
+ expect(validTable).toBe(true);
+ });
+ });
+});
+
+describe('Test Queue tester traits when reports exist', () => {
+ it('renders page h1', async () => {
+ await getPage({ role: 'tester', url: '/test-queue' }, async page => {
+ const h1Element = await text(page, 'h1');
+ expect(h1Element).toBe('Test Queue');
+ });
+ });
+
+ it('renders page with instructions', async () => {
+ await getPage({ role: 'tester', url: '/test-queue' }, async page => {
+ const instructionsSelector = '[data-testid="test-queue-instructions"]';
+ const instructionsText = await text(page, instructionsSelector);
+
+ expect(instructionsText).toBe(
+ 'Assign yourself a test plan or start executing one that is already assigned to you.'
+ );
+ });
+ });
+
+ it('renders page with known pattern sections', async () => {
+ await getPage({ role: 'tester', url: '/test-queue' }, async page => {
+ const alertSectionHeaderSelector = 'h2 ::-p-text(Alert Example)';
+ const alertSectionContainerSelector = 'div#disclosure-container-alert-0';
+ const modalDialogSectionHeaderSelector =
+ 'h2 ::-p-text(Modal Dialog Example)';
+ const modalDialogSectionContainerSelector =
+ 'div#disclosure-container-modal-dialog-0';
+
+ const alertSectionTitle = await text(page, alertSectionHeaderSelector);
+ const alertSectionDisplay = await display(
+ page,
+ alertSectionContainerSelector
+ );
+ const modalDialogSectionTitle = await text(
+ page,
+ modalDialogSectionHeaderSelector
+ );
+ const modalDialogSectionDisplay = await display(
+ page,
+ modalDialogSectionContainerSelector
+ );
+
+ expect(alertSectionTitle).toBe('Alert Example');
+ expect(alertSectionDisplay).toBe('none');
+ expect(modalDialogSectionTitle).toBe('Modal Dialog Example');
+ expect(modalDialogSectionDisplay).toBe('none');
+ });
+ });
+
+ it("renders page and open pattern section's table", async () => {
+ await getPage({ role: 'tester', url: '/test-queue' }, async page => {
+ const modalDialogSectionContainerSelector =
+ 'div#disclosure-container-modal-dialog-0';
+ const modalDialogSectionButtonSelector =
+ 'button#disclosure-btn-modal-dialog-0';
+ const modalDialogTableSelector =
+ 'table[aria-label="Reports for Modal Dialog Example V24.06.07 in draft phase"]';
+
+ await page.waitForSelector(modalDialogSectionButtonSelector);
+
+ const modalDialogSectionButton = await text(
+ page,
+ modalDialogSectionContainerSelector
+ );
+ const preClickModalDialogSectionDisplay = await display(
+ page,
+ modalDialogSectionContainerSelector
+ );
+
+ // Expand Modal Dialog's V24.06.07 section
+ await page.click(modalDialogSectionButtonSelector);
+ const postClickModalDialogSectionDisplay = await display(
+ page,
+ modalDialogSectionContainerSelector
+ );
+
+ // Wait for the table to render
+ await page.waitForSelector(modalDialogTableSelector);
+
+ // Check if the table contains expected column data
+ const validTable = await page.$eval(modalDialogTableSelector, el => {
+ const sanitizedText = text =>
+ text
+ .replaceAll(String.fromCharCode(160), ' ') // remove being included
+ .trim();
+
+ // Check if any cell in the table contains expected text
+ const cells = Array.from(el.querySelectorAll('td'));
+
+ // Assistive Technology Column
+ const atColumn = cells[0];
+ const atColumnText = sanitizedText(atColumn.innerText);
+ const atColumnCondition = atColumnText.includes('NVDA 2020.4 or later');
+
+ // Browser Column
+ const browserColumn = cells[1];
+ const browserColumnText = sanitizedText(browserColumn.innerText);
+ const browserColumnCondition =
+ browserColumnText.includes('Chrome Any version');
+
+ // Testers Column
+ const testersColumn = cells[2];
+ const testersColumnText = sanitizedText(testersColumn.innerText);
+ const testersColumnCondition =
+ !testersColumnText.includes('Assign Testers') && // doesn't show unless admin
+ testersColumnText.includes('Assign Yourself') &&
+ testersColumnText.includes('esmeralda-baggins') &&
+ testersColumnText.includes('tests complete');
+
+ // Status Column
+ const statusColumn = cells[3];
+ const statusColumnText = sanitizedText(statusColumn.innerText);
+ const statusColumnCondition = statusColumnText.includes(
+ '100% complete by esmeralda-baggins with 0 conflicts'
+ );
+
+ // Actions Column
+ const actionsColumn = cells[4];
+ const actionsColumnText = sanitizedText(actionsColumn.innerText);
+ const actionsColumnCondition =
+ actionsColumnText.includes('Start Testing') &&
+ !actionsColumnText.includes('Open run as...') &&
+ !actionsColumnText.includes('Mark as Final') &&
+ !actionsColumnText.includes('Mark as Final') &&
+ !actionsColumnText.includes('Manage NVDA Bot Run');
+
+ return (
+ atColumnCondition &&
+ browserColumnCondition &&
+ testersColumnCondition &&
+ statusColumnCondition &&
+ actionsColumnCondition
+ );
+ });
+
+ expect(modalDialogSectionButton.includes('V24.06.07')).toBe(true);
+ expect(preClickModalDialogSectionDisplay).toBe('none');
+ expect(postClickModalDialogSectionDisplay).toBe('block');
+ expect(validTable).toBe(true);
+ });
+ });
+
+ it("renders page, opens pattern section's table and assigns yourself and shows proper state of context related action", async () => {
+ await getPage({ role: 'tester', url: '/test-queue' }, async page => {
+ const modalDialogSectionButtonSelector =
+ 'button#disclosure-btn-modal-dialog-0';
+ const modalDialogTableSelector =
+ 'table[aria-label="Reports for Modal Dialog Example V24.06.07 in draft phase"]';
+
+ await page.waitForSelector(modalDialogSectionButtonSelector);
+
+ // Expand Modal Dialog's V24.06.07 section
+ await page.click(modalDialogSectionButtonSelector);
+
+ // Wait for the table to render
+ await page.waitForSelector(modalDialogTableSelector);
+
+ const preAssignValidTable = await page.$eval(
+ modalDialogTableSelector,
+ el => {
+ const sanitizedText = text =>
+ text
+ .replaceAll(String.fromCharCode(160), ' ') // remove being included
+ .trim();
+
+ // Check if any cell in the table contains expected text
+ const cells = Array.from(el.querySelectorAll('td'));
+
+ // Actions Column
+ const actionsColumn = cells[4];
+
+ // Normal disabled button unless assigned
+ const startTestingButton = actionsColumn.querySelector('button');
+ const startTestingButtonText = sanitizedText(
+ startTestingButton.innerText
+ );
+ const actionsColumnText = sanitizedText(actionsColumn.innerText);
+ return (
+ startTestingButton.disabled &&
+ startTestingButtonText.includes('Start Testing') &&
+ !actionsColumnText.includes('Open run as...') &&
+ !actionsColumnText.includes('Mark as Final') &&
+ !actionsColumnText.includes('Mark as Final') &&
+ !actionsColumnText.includes('Manage NVDA Bot Run')
+ );
+ }
+ );
+
+ const assignYourselfButton = await page.evaluateHandle(() => {
+ const modalDialogTableSelector =
+ 'table[aria-label="Reports for Modal Dialog Example V24.06.07 in draft phase"]';
+ const modalDialogTable = document.querySelector(
+ modalDialogTableSelector
+ );
+
+ // Check if any cell in the table contains expected text
+ const cells = Array.from(modalDialogTable.querySelectorAll('td'));
+
+ // Testers Column
+ const testersColumn = cells[2];
+
+ // First button will be the 'Assign Yourself' button
+ return testersColumn.querySelector('button');
+ });
+
+ // Assign yourself to the test plan report
+ await assignYourselfButton.click();
+
+ // Allow self assignment to happen
+ await new Promise(resolve => setTimeout(resolve, 2000));
+
+ // Check if the table contains expected column data
+ const postAssignValidTable = await page.$eval(
+ modalDialogTableSelector,
+ el => {
+ const sanitizedText = text =>
+ text
+ .replaceAll(String.fromCharCode(160), ' ') // remove being included
+ .trim();
+
+ // Check if any cell in the table contains expected text
+ const cells = Array.from(el.querySelectorAll('td'));
+
+ // Actions Column
+ const actionsColumn = cells[4];
+
+ // The button gets converted to
+ const startTestingButton =
+ actionsColumn.querySelector('a[role="button"]');
+ const startTestingButtonText = sanitizedText(
+ startTestingButton.innerText
+ );
+ const actionsColumnText = sanitizedText(actionsColumn.innerText);
+ return (
+ startTestingButton &&
+ startTestingButtonText.includes('Start Testing') &&
+ !actionsColumnText.includes('Open run as...') &&
+ !actionsColumnText.includes('Mark as Final') &&
+ !actionsColumnText.includes('Mark as Final') &&
+ !actionsColumnText.includes('Manage NVDA Bot Run')
+ );
+ }
+ );
+
+ expect(preAssignValidTable).toBe(true);
+ expect(postAssignValidTable).toBe(true);
+ });
+ });
+});
diff --git a/client/tests/e2e/smokeTest.e2e.test.js b/client/tests/e2e/smokeTest.e2e.test.js
new file mode 100644
index 000000000..b49f8bbd5
--- /dev/null
+++ b/client/tests/e2e/smokeTest.e2e.test.js
@@ -0,0 +1,97 @@
+import getPage from '../util/getPage';
+
+describe('smoke test', () => {
+ it('end-to-end tests can simultaneously sign in with all roles', async () => {
+ await Promise.all([
+ getPage({ role: 'admin', url: '/test-queue' }, async page => {
+ // Only admins can remove rows from the test queue
+ await page.waitForSelector(
+ 'td [type="button"] ::-p-text(Delete Report)'
+ );
+ }),
+
+ getPage({ role: 'tester', url: '/test-queue' }, async page => {
+ // Testers can assign themselves
+ await page.waitForSelector('table ::-p-text(Assign Yourself)');
+ const adminOnlyRemoveButton = await page.$(
+ 'td [type="button"] ::-p-text(Delete Report)'
+ );
+ expect(adminOnlyRemoveButton).toBe(null);
+ }),
+
+ getPage(
+ { role: 'vendor', url: '/test-queue' },
+ async (page, { baseUrl }) => {
+ // Vendors get the same test queue as signed-out users
+ await page.waitForSelector('button ::-p-text(V22.04.14)');
+ await page.click('button ::-p-text(V22.04.14)');
+
+ await page.waitForSelector('td [role="button"]');
+ const buttonText = await page.$eval(
+ 'td [role="button"]',
+ button => button.textContent
+ );
+ expect(buttonText).toEqual('View Tests');
+
+ // Unlike signed-out users, they will get tables on this page
+ await page.goto(`${baseUrl}/candidate-review`);
+ await page.waitForSelector('table');
+ }
+ ),
+
+ getPage({ role: false, url: '/test-queue' }, async page => {
+ // Signed-out users can only view tests, not run them
+ await page.waitForSelector('td [role="button"] ::-p-text(View Tests)');
+ })
+ ]);
+ });
+
+ it('loads various pages without crashing', async () => {
+ await Promise.all([
+ getPage({ role: false, url: '/' }, async page => {
+ await page.waitForSelector('h1');
+ const h1Handle = await page.waitForSelector('h1');
+ const h1Text = await h1Handle.evaluate(h1 => h1.innerText);
+ expect(h1Text).toBe(
+ 'Enabling Interoperability for Assistive Technology Users'
+ );
+ }),
+ getPage({ role: false, url: '/reports' }, async page => {
+ // Wait for an h2 because an h1 will show while the page is
+ // still loading
+ await page.waitForSelector('h2');
+ const h1Handle = await page.waitForSelector('h1');
+ const h1Text = await h1Handle.evaluate(h1 => h1.innerText);
+ expect(h1Text).toBe('Assistive Technology Interoperability Reports');
+ }),
+ getPage({ role: false, url: '/data-management' }, async page => {
+ // Wait for an h2 because an h1 will show while the page is
+ // still loading
+ await page.waitForSelector('h2');
+ const h1Handle = await page.waitForSelector('h1');
+ const h1Text = await h1Handle.evaluate(h1 => h1.innerText);
+ expect(h1Text).toBe('Data Management');
+ }),
+ getPage({ role: false, url: '/test-plan-report/15' }, async page => {
+ // Wait for an h2 because an h1 will show while the page is
+ // still loading
+ await page.waitForSelector('h2');
+ const h1Handle = await page.waitForSelector('h1');
+ const h1Text = await h1Handle.evaluate(h1 => h1.innerText);
+ expect(h1Text).toBe(
+ 'Test 1:\nNavigate forwards to a not pressed toggle button'
+ );
+ }),
+ getPage({ role: 'admin', url: '/test-plan-report/15' }, async page => {
+ // Wait for an h2 because an h1 will show while the page is
+ // still loading
+ await page.waitForSelector('h2');
+ const h1Handle = await page.waitForSelector('h1');
+ const h1Text = await h1Handle.evaluate(h1 => h1.innerText);
+ expect(h1Text).toBe(
+ 'Test 1:\nNavigate forwards to a not pressed toggle button'
+ );
+ })
+ ]);
+ });
+});
diff --git a/client/tests/smokeTest.test.js b/client/tests/smokeTest.test.js
deleted file mode 100644
index 3818b1d3b..000000000
--- a/client/tests/smokeTest.test.js
+++ /dev/null
@@ -1,117 +0,0 @@
-import getPage from './util/getPage';
-
-describe('smoke test', () => {
- it('end-to-end tests can simultaneously sign in with all roles [old]', async () => {
- await Promise.all([
- getPage({ role: 'admin', url: '/test-queue-old' }, async page => {
- // Only admins can remove rows from the test queue
- await page.waitForSelector('td.actions ::-p-text(Remove)');
- }),
-
- getPage({ role: 'tester', url: '/test-queue-old' }, async page => {
- // Testers can assign themselves
- await page.waitForSelector('table ::-p-text(Assign Yourself)');
- const adminOnlyRemoveButton = await page.$(
- 'td.actions ::-p-text(Remove)'
- );
- expect(adminOnlyRemoveButton).toBe(null);
- }),
-
- getPage(
- { role: 'vendor', url: '/test-queue-old' },
- async (page, { baseUrl }) => {
- // Vendors get the same test queue as signed-out users
- await page.waitForSelector(
- 'td.actions ::-p-text(View tests)'
- );
- // Unlike signed-out users, they will get tables on this page
- await page.goto(`${baseUrl}/candidate-review`);
- await page.waitForSelector('table');
- }
- ),
-
- getPage({ role: false, url: '/test-queue-old' }, async page => {
- // Signed-out users can only view tests, not run them
- await page.waitForSelector('td.actions ::-p-text(View tests)');
- })
- ]);
- });
-
- it('end-to-end tests can simultaneously sign in with all roles', async () => {
- await Promise.all([
- getPage({ role: 'admin', url: '/test-queue' }, async page => {
- // Only admins can remove rows from the test queue
- await page.waitForSelector(
- 'td [type="button"] ::-p-text(Delete Report)'
- );
- }),
-
- getPage({ role: 'tester', url: '/test-queue' }, async page => {
- // Testers can assign themselves
- await page.waitForSelector('table ::-p-text(Assign Yourself)');
- const adminOnlyRemoveButton = await page.$(
- 'td [type="button"] ::-p-text(Delete Report)'
- );
- expect(adminOnlyRemoveButton).toBe(null);
- }),
-
- getPage(
- { role: 'vendor', url: '/test-queue' },
- async (page, { baseUrl }) => {
- // Vendors get the same test queue as signed-out users
- await page.waitForSelector('button ::-p-text(V22.04.14)');
- await page.click('button ::-p-text(V22.04.14)');
-
- await page.waitForSelector('td [role="button"]');
- const buttonText = await page.$eval(
- 'td [role="button"]',
- button => button.textContent
- );
- expect(buttonText).toEqual('View Tests');
-
- // Unlike signed-out users, they will get tables on this page
- await page.goto(`${baseUrl}/candidate-review`);
- await page.waitForSelector('table');
- }
- ),
-
- getPage({ role: false, url: '/test-queue' }, async page => {
- // Signed-out users can only view tests, not run them
- await page.waitForSelector(
- 'td [role="button"] ::-p-text(View Tests)'
- );
- })
- ]);
- });
-
- it('loads various pages without crashing', async () => {
- await Promise.all([
- getPage({ role: false, url: '/' }, async page => {
- await page.waitForSelector('h1');
- const h1Handle = await page.waitForSelector('h1');
- const h1Text = await h1Handle.evaluate(h1 => h1.innerText);
- expect(h1Text).toBe(
- 'Enabling Interoperability for Assistive Technology Users'
- );
- }),
- getPage({ role: false, url: '/reports' }, async page => {
- // Wait for an h2 because an h1 will show while the page is
- // still loading
- await page.waitForSelector('h2');
- const h1Handle = await page.waitForSelector('h1');
- const h1Text = await h1Handle.evaluate(h1 => h1.innerText);
- expect(h1Text).toBe(
- 'Assistive Technology Interoperability Reports'
- );
- }),
- getPage({ role: false, url: '/data-management' }, async page => {
- // Wait for an h2 because an h1 will show while the page is
- // still loading
- await page.waitForSelector('h2');
- const h1Handle = await page.waitForSelector('h1');
- const h1Text = await h1Handle.evaluate(h1 => h1.innerText);
- expect(h1Text).toBe('Data Management');
- })
- ]);
- });
-});
diff --git a/client/tests/util/getPage.js b/client/tests/util/getPage.js
index 6ce14f783..8e841d7c5 100644
--- a/client/tests/util/getPage.js
+++ b/client/tests/util/getPage.js
@@ -15,98 +15,96 @@ const AUTOMATION_SCHEDULER_PORT = 8833;
const baseUrl = `http://localhost:${CLIENT_PORT}`;
const startServer = async serverOrClient => {
- return new Promise(resolve => {
- const server = spawn(
- 'yarn',
- serverOrClient === 'server'
- ? ['workspace', 'server', 'dev-debug']
- : ['workspace', 'client', 'dev'],
- {
- cwd: path.resolve(__dirname, '../../'),
- env: {
- PATH: process.env.PATH,
- PORT,
- CLIENT_PORT,
- AUTOMATION_SCHEDULER_PORT,
- API_SERVER: `http://localhost:${PORT}`,
- APP_SERVER: baseUrl,
- AUTOMATION_SCHEDULER_URL: `http://localhost:${AUTOMATION_SCHEDULER_PORT}`,
- PGDATABASE: 'aria_at_report_test',
- PGPORT: 5432,
- ENVIRONMENT: 'test'
- }
- }
- );
-
- server.on('error', error => {
- throw new Error('Error raised by startServer process', {
- cause: error
- });
- });
+ return new Promise(resolve => {
+ const server = spawn(
+ 'yarn',
+ serverOrClient === 'server'
+ ? ['workspace', 'server', 'dev-debug']
+ : ['workspace', 'client', 'dev'],
+ {
+ cwd: path.resolve(__dirname, '../../'),
+ env: {
+ PATH: process.env.PATH,
+ PORT,
+ CLIENT_PORT,
+ AUTOMATION_SCHEDULER_PORT,
+ API_SERVER: `http://localhost:${PORT}`,
+ APP_SERVER: baseUrl,
+ AUTOMATION_SCHEDULER_URL: `http://localhost:${AUTOMATION_SCHEDULER_PORT}`,
+ PGDATABASE: 'aria_at_report_test',
+ PGPORT: 5432,
+ ENVIRONMENT: 'test'
+ }
+ }
+ );
- server.on('exit', (code, signal) => {
- if (code) {
- console.error('startServer exited with code', code);
- } else if (signal) {
- console.error('startServer was killed with signal', signal);
- } else {
- console.info('startServer exited with no errors.'); // eslint-disable-line no-console
- }
- });
+ server.on('error', error => {
+ throw new Error('Error raised by startServer process', {
+ cause: error
+ });
+ });
- const killServer = async () => {
- await new Promise((resolve, reject) => {
- treeKill(server.pid, error => {
- if (error) return reject(error);
- resolve();
- });
- });
- };
-
- server.stdout.on('data', data => {
- const output = stripAnsi(data.toString());
- console.info(output); // eslint-disable-line no-console
-
- if (
- (serverOrClient === 'server' &&
- output.includes(`Listening on ${PORT}`)) ||
- (serverOrClient === 'client' &&
- output.includes('compiled successfully'))
- ) {
- resolve({ close: killServer });
- }
- });
+ server.on('exit', (code, signal) => {
+ if (code) {
+ console.error('startServer exited with code', code);
+ } else if (signal) {
+ console.error('startServer was killed with signal', signal);
+ } else {
+ console.info('startServer exited with no errors.'); // eslint-disable-line no-console
+ }
+ });
- server.stderr.on('data', data => {
- const output = data.toString();
- console.info(output); // eslint-disable-line no-console
+ const killServer = async () => {
+ await new Promise((resolve, reject) => {
+ treeKill(server.pid, error => {
+ if (error) return reject(error);
+ resolve();
});
+ });
+ };
+
+ server.stdout.on('data', data => {
+ const output = stripAnsi(data.toString());
+ console.info(output); // eslint-disable-line no-console
+
+ if (
+ (serverOrClient === 'server' &&
+ output.includes(`Listening on ${PORT}`)) ||
+ (serverOrClient === 'client' &&
+ output.includes('compiled successfully'))
+ ) {
+ resolve({ close: killServer });
+ }
});
+
+ server.stderr.on('data', data => {
+ const output = data.toString();
+ console.info(output); // eslint-disable-line no-console
+ });
+ });
};
const setup = async () => {
- // eslint-disable-next-line no-console
- console.info(
- 'Starting dev servers. This is required for end-to-end testing'
- );
-
- [clientServer, backendServer] = await Promise.all([
- startServer('client'),
- startServer('server')
- ]);
-
- return puppeteer.launch({
- headless: isDebugMode ? false : 'new',
- args: ['--no-sandbox'], // Required for GitHub environment
- devtools: isDebugMode // Allows `debugger;` statements to work
- });
+ // eslint-disable-next-line no-console
+ console.info('Starting dev servers. This is required for end-to-end testing');
+
+ [clientServer, backendServer] = await Promise.all([
+ startServer('client'),
+ startServer('server')
+ ]);
+
+ return puppeteer.launch({
+ headless: isDebugMode ? false : 'new',
+ args: ['--no-sandbox'], // Required for GitHub environment
+ devtools: isDebugMode // Allows `debugger;` statements to work
+ });
};
const teardown = async () => {
- await Promise.all([backendServer.close(), clientServer.close()]);
+ await Promise.all([backendServer.close(), clientServer.close()]);
- // Browser might not be defined, if it failed to start
- if (global.browser) await global.browser.close();
+ // Browser might not be defined, if it failed to start
+ if (global.browser) await global.browser.close();
};
let incognitoContexts = {};
@@ -123,60 +121,60 @@ let incognitoContexts = {};
* needed for navigation.
*/
const getPage = async (options, callback) => {
- const { role, url } = options;
- if (role == null || !['admin', 'tester', 'vendor', false].includes(role)) {
- throw new Error('Please provide a valid role');
- }
-
- const foundExistingIncognitoContext = !!incognitoContexts[role];
+ const { role, url } = options;
+ if (role == null || !['admin', 'tester', 'vendor', false].includes(role)) {
+ throw new Error('Please provide a valid role');
+ }
- if (!foundExistingIncognitoContext) {
- incognitoContexts[role] =
- await global.browser.createIncognitoBrowserContext();
- }
- const incognitoContext = incognitoContexts[role];
+ const foundExistingIncognitoContext = !!incognitoContexts[role];
- const page = await incognitoContext.newPage();
+ if (!foundExistingIncognitoContext) {
+ incognitoContexts[role] =
+ await global.browser.createIncognitoBrowserContext();
+ }
+ const incognitoContext = incognitoContexts[role];
- if (!url) {
- throw new Error('Please provide a URL, even if it it is simply "/"');
- }
+ const page = await incognitoContext.newPage();
- if (isDebugMode) {
- page.setDefaultTimeout(604800000 /* a week should be enough */);
- }
+ if (!url) {
+ throw new Error('Please provide a URL, even if it it is simply "/"');
+ }
- await page.goto(`${baseUrl}${url}`);
+ if (isDebugMode) {
+ page.setDefaultTimeout(604800000 /* a week should be enough */);
+ }
- await page.waitForNetworkIdle();
+ await page.goto(`${baseUrl}${url}`);
- await page.evaluate('startTestTransaction()');
+ await page.waitForNetworkIdle();
- if (role && !foundExistingIncognitoContext) {
- await page.waitForSelector('::-p-text(Sign in with GitHub)');
+ await page.evaluate('startTestTransaction()');
- const username = `joe-the-${role}`;
+ if (role && !foundExistingIncognitoContext) {
+ await page.waitForSelector('::-p-text(Sign in with GitHub)');
- if (role === 'admin') {
- await page.evaluate(`signMeInAsAdmin("${username}")`);
- }
- if (role === 'tester') {
- await page.evaluate(`signMeInAsTester("${username}")`);
- }
- if (role === 'vendor') {
- await page.evaluate(`signMeInAsVendor("${username}")`);
- }
+ const username = `joe-the-${role}`;
- await page.waitForSelector('::-p-text(Signed in)');
+ if (role === 'admin') {
+ await page.evaluate(`signMeInAsAdmin("${username}")`);
}
-
- try {
- await callback(page, { baseUrl });
- } finally {
- await page.evaluate('endTestTransaction()');
+ if (role === 'tester') {
+ await page.evaluate(`signMeInAsTester("${username}")`);
}
+ if (role === 'vendor') {
+ await page.evaluate(`signMeInAsVendor("${username}")`);
+ }
+
+ await page.waitForSelector('::-p-text(Signed in)');
+ }
+
+ try {
+ await callback(page, { baseUrl });
+ } finally {
+ await page.evaluate('endTestTransaction()');
+ }
- await page.close();
+ await page.close();
};
module.exports = getPage;
diff --git a/client/tests/util/index.js b/client/tests/util/index.js
index a527bf716..96c576e9f 100644
--- a/client/tests/util/index.js
+++ b/client/tests/util/index.js
@@ -1,3 +1,3 @@
export const findByTestAttr = function (wrapper, val) {
- return wrapper.find(`[data-test="${val}"]`);
+ return wrapper.find(`[data-test="${val}"]`);
};
diff --git a/client/tests/util/jestGlobalSetup.js b/client/tests/util/jestGlobalSetup.js
index 192c96945..18b1276ba 100644
--- a/client/tests/util/jestGlobalSetup.js
+++ b/client/tests/util/jestGlobalSetup.js
@@ -6,18 +6,18 @@ const { setup } = require('./getPage');
const isDebugMode = process.argv.some(arg => arg.startsWith('--testTimeout'));
module.exports = async () => {
- const browser = await setup();
+ const browser = await setup();
- // See https://jestjs.io/docs/puppeteer for more information
+ // See https://jestjs.io/docs/puppeteer for more information
- // store the browser instance so we can teardown it later
- // this global is only available in the teardown but not in TestEnvironments
- global.browser = browser;
+ // store the browser instance so we can teardown it later
+ // this global is only available in the teardown but not in TestEnvironments
+ global.browser = browser;
- if (!isDebugMode) {
- // use the file system to expose the wsEndpoint for TestEnvironments
- const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
- await mkdir(DIR, { recursive: true });
- await writeFile(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint());
- }
+ if (!isDebugMode) {
+ // use the file system to expose the wsEndpoint for TestEnvironments
+ const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
+ await mkdir(DIR, { recursive: true });
+ await writeFile(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint());
+ }
};
diff --git a/client/tests/util/jestGlobalTeardown.js b/client/tests/util/jestGlobalTeardown.js
index bd59a209f..a2b66a252 100644
--- a/client/tests/util/jestGlobalTeardown.js
+++ b/client/tests/util/jestGlobalTeardown.js
@@ -4,10 +4,10 @@ const path = require('path');
const { teardown } = require('./getPage');
module.exports = async () => {
- // See https://jestjs.io/docs/puppeteer for more information
- // clean-up the wsEndpoint file
- const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
- await fs.rm(DIR, { recursive: true, force: true });
+ // See https://jestjs.io/docs/puppeteer for more information
+ // clean-up the wsEndpoint file
+ const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
+ await fs.rm(DIR, { recursive: true, force: true });
- await teardown();
+ await teardown();
};
diff --git a/client/tests/util/jestPuppeteer.js b/client/tests/util/jestPuppeteer.js
index c9a25f9c1..337fa5f17 100644
--- a/client/tests/util/jestPuppeteer.js
+++ b/client/tests/util/jestPuppeteer.js
@@ -11,40 +11,40 @@ const isDebugMode = process.argv.some(arg => arg.startsWith('--testTimeout'));
const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
class PuppeteerEnvironment extends NodeEnvironment {
- constructor(config) {
- super(config);
+ constructor(config) {
+ super(config);
+ }
+
+ async setup() {
+ await super.setup();
+ // get the wsEndpoint
+ const wsEndpoint = await readFile(path.join(DIR, 'wsEndpoint'), 'utf8');
+ if (!wsEndpoint) {
+ throw new Error('wsEndpoint not found');
}
- async setup() {
- await super.setup();
- // get the wsEndpoint
- const wsEndpoint = await readFile(path.join(DIR, 'wsEndpoint'), 'utf8');
- if (!wsEndpoint) {
- throw new Error('wsEndpoint not found');
- }
-
- // connect to puppeteer
- this.global.browser = await puppeteer.connect({
- browserWSEndpoint: wsEndpoint
- });
- }
+ // connect to puppeteer
+ this.global.browser = await puppeteer.connect({
+ browserWSEndpoint: wsEndpoint
+ });
+ }
- async teardown() {
- if (this.global.browser) {
- this.global.browser.disconnect();
- }
- await super.teardown();
+ async teardown() {
+ if (this.global.browser) {
+ this.global.browser.disconnect();
}
+ await super.teardown();
+ }
- getVmContext() {
- return super.getVmContext();
- }
+ getVmContext() {
+ return super.getVmContext();
+ }
}
class DoNothingEnvironment extends NodeEnvironment {
- constructor(config) {
- super(config);
- }
+ constructor(config) {
+ super(config);
+ }
}
module.exports = isDebugMode ? DoNothingEnvironment : PuppeteerEnvironment;
diff --git a/client/tests/util/stripAnsi.js b/client/tests/util/stripAnsi.js
index 1480dd458..302a8aa7b 100644
--- a/client/tests/util/stripAnsi.js
+++ b/client/tests/util/stripAnsi.js
@@ -1,12 +1,12 @@
// borrowed from ansi-regex / strip-ansi / chalk but reproduced due to ESM vs require
function ansiRegex({ onlyFirst = false } = {}) {
- const pattern = [
- '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
- '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
- ].join('|');
+ const pattern = [
+ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
+ '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
+ ].join('|');
- return new RegExp(pattern, onlyFirst ? undefined : 'g');
+ return new RegExp(pattern, onlyFirst ? undefined : 'g');
}
const re = ansiRegex();
diff --git a/client/utils/DisplayNone.jsx b/client/utils/DisplayNone.jsx
index 6a3888df4..83f7322fb 100644
--- a/client/utils/DisplayNone.jsx
+++ b/client/utils/DisplayNone.jsx
@@ -2,11 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
const DisplayNone = ({ children }) => {
- return {children}
;
+ return {children}
;
};
DisplayNone.propTypes = {
- children: PropTypes.node.isRequired
+ children: PropTypes.node.isRequired
};
export default DisplayNone;
diff --git a/client/utils/ScrollFixer.jsx b/client/utils/ScrollFixer.jsx
index aec5334f4..3f84f6d5b 100644
--- a/client/utils/ScrollFixer.jsx
+++ b/client/utils/ScrollFixer.jsx
@@ -9,61 +9,61 @@ import { useLocation } from 'react-router';
* that it must be inside React Router's Router component.
*/
const ScrollFixer = ({ children }) => {
- const location = useLocation();
+ const location = useLocation();
- useLayoutEffect(() => {
- const scrollTop = () => {
- window.scroll(0, 0);
- // When switching pages, the focus should jump to the top. Otherwise
- // screen readers' focus might be lingering in a nonsensical
- // location partly down the page.
- document.querySelector('a').focus();
- };
- if (!location.hash) return scrollTop();
- (async () => {
- // The point at which the window jumping down the page would become
- // disorienting. This must include time for the page's API requests
- // to complete.
- const timeout = 8000;
- const element = await pollForElement(location.hash, { timeout });
- if (!element) return scrollTop();
- element.scrollIntoView();
- element.focus();
- })();
- }, [location]);
+ useLayoutEffect(() => {
+ const scrollTop = () => {
+ window.scroll(0, 0);
+ // When switching pages, the focus should jump to the top. Otherwise
+ // screen readers' focus might be lingering in a nonsensical
+ // location partly down the page.
+ document.querySelector('a').focus();
+ };
+ if (!location.hash) return scrollTop();
+ (async () => {
+ // The point at which the window jumping down the page would become
+ // disorienting. This must include time for the page's API requests
+ // to complete.
+ const timeout = 8000;
+ const element = await pollForElement(location.hash, { timeout });
+ if (!element) return scrollTop();
+ element.scrollIntoView();
+ element.focus();
+ })();
+ }, [location]);
- return children;
+ return children;
};
ScrollFixer.propTypes = {
- children: PropTypes.node.isRequired
+ children: PropTypes.node.isRequired
};
const pollForElement = async (selector, { timeout }) => {
- let element = document.querySelector(selector);
- if (element) return element;
+ let element = document.querySelector(selector);
+ if (element) return element;
- let timeoutExceeded = false;
- window.setTimeout(() => {
- timeoutExceeded = true;
- }, timeout);
+ let timeoutExceeded = false;
+ window.setTimeout(() => {
+ timeoutExceeded = true;
+ }, timeout);
- return new Promise(resolve => {
- const interval = window.setInterval(() => {
- element = document.querySelector(selector);
+ return new Promise(resolve => {
+ const interval = window.setInterval(() => {
+ element = document.querySelector(selector);
- if (element) {
- clearInterval(interval);
- resolve(element);
- return;
- }
+ if (element) {
+ clearInterval(interval);
+ resolve(element);
+ return;
+ }
- if (timeoutExceeded) {
- clearInterval(interval);
- resolve(null);
- }
- }, 10);
- });
+ if (timeoutExceeded) {
+ clearInterval(interval);
+ resolve(null);
+ }
+ }, 10);
+ });
};
export default ScrollFixer;
diff --git a/client/utils/alphabetizeObjectBy.js b/client/utils/alphabetizeObjectBy.js
index 9d4968f36..5a26a2319 100644
--- a/client/utils/alphabetizeObjectBy.js
+++ b/client/utils/alphabetizeObjectBy.js
@@ -11,12 +11,12 @@
* @returns - The same object but with its keys sorted.
*/
const alphabetizeObjectBy = (object, getString) => {
- return Object.fromEntries(
- Object.entries(object).sort((a, b) => {
- // https://stackoverflow.com/a/45544166/3888572
- return getString(a).localeCompare(getString(b));
- })
- );
+ return Object.fromEntries(
+ Object.entries(object).sort((a, b) => {
+ // https://stackoverflow.com/a/45544166/3888572
+ return getString(a).localeCompare(getString(b));
+ })
+ );
};
export default alphabetizeObjectBy;
diff --git a/client/utils/alphabetizeObjectBy.test.js b/client/utils/alphabetizeObjectBy.test.js
index 419e3ff4d..8b801727d 100644
--- a/client/utils/alphabetizeObjectBy.test.js
+++ b/client/utils/alphabetizeObjectBy.test.js
@@ -1,22 +1,22 @@
import alphabetizeObjectBy from './alphabetizeObjectBy';
describe('alphabetizeObjectBy', () => {
- it('alphabetizes an object', () => {
- const before = { z: 1, y: 2, x: 3 };
- const after = alphabetizeObjectBy(before, ([key]) => key);
- expect(Object.entries(after)).toEqual([
- ['x', 3],
- ['y', 2],
- ['z', 1]
- ]);
- });
+ it('alphabetizes an object', () => {
+ const before = { z: 1, y: 2, x: 3 };
+ const after = alphabetizeObjectBy(before, ([key]) => key);
+ expect(Object.entries(after)).toEqual([
+ ['x', 3],
+ ['y', 2],
+ ['z', 1]
+ ]);
+ });
- it('returns a shallow copy', () => {
- const identicalObject = {};
- const before = { c: 1, b: 2, a: identicalObject };
- const after = alphabetizeObjectBy(before, ([key]) => key);
- identicalObject.testAddingAProperty = true;
- expect(after.a).toBe(identicalObject);
- expect(after.a.testAddingAProperty).toBe(true);
- });
+ it('returns a shallow copy', () => {
+ const identicalObject = {};
+ const before = { c: 1, b: 2, a: identicalObject };
+ const after = alphabetizeObjectBy(before, ([key]) => key);
+ identicalObject.testAddingAProperty = true;
+ expect(after.a).toBe(identicalObject);
+ expect(after.a.testAddingAProperty).toBe(true);
+ });
});
diff --git a/client/utils/aria.js b/client/utils/aria.js
index 6118c86f5..6a9b97d8e 100644
--- a/client/utils/aria.js
+++ b/client/utils/aria.js
@@ -1,25 +1,25 @@
export const evaluateAtNameKey = atName => {
- // Could probably add back support for AT keys from the database level
- if (atName.toLowerCase().includes('voiceover')) return 'voiceover_macos';
- else return atName.toLowerCase();
+ // Could probably add back support for AT keys from the database level
+ if (atName.toLowerCase().includes('voiceover')) return 'voiceover_macos';
+ else return atName.toLowerCase();
};
export const buildTestPageUrl = (gitSha, directory, testReferencePath) => {
- const BASE_PATH = '/aria-at';
- return `${BASE_PATH}/${gitSha}/build/tests/${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';
- case 'DEPRECATED':
- return 'Deprecated';
- }
+ switch (name) {
+ case 'RD':
+ return 'R&D';
+ case 'DRAFT':
+ return 'Draft';
+ case 'CANDIDATE':
+ return 'Candidate';
+ case 'RECOMMENDED':
+ return 'Recommended';
+ case 'DEPRECATED':
+ return 'Deprecated';
+ }
};
diff --git a/client/utils/automation.js b/client/utils/automation.js
index 71d3d978e..208091473 100644
--- a/client/utils/automation.js
+++ b/client/utils/automation.js
@@ -1,16 +1,79 @@
+/**
+ * Checks if the AT, browser, and AT version requirements are supported by the automation
+ * @param {object} ctx
+ * @param {object} ctx.at
+ * @param {object} ctx.browser
+ * @param {object} ctx.minimumAtVersion optional
+ * @param {object} ctx.exactAtVersion optional
+ * @returns {boolean}
+ */
export const isSupportedByResponseCollector = ctx => {
- if (!ctx || !ctx.at || !ctx.browser) {
- return false;
- }
- const {
- at: { key: atKey },
- browser: { key: browserKey }
- } = ctx;
- return (
- (atKey === 'nvda' &&
- (browserKey === 'chrome' || browserKey === 'firefox')) ||
- (atKey === 'voiceover_macos' && browserKey === 'safari_macos')
+ if (!ctx || !ctx.at || !ctx.browser) {
+ return false;
+ }
+
+ const { at, browser } = ctx;
+ const { key: atKey } = at;
+ const { key: browserKey } = browser;
+
+ // Check if the AT and browser are supported by the automation
+ const isNvdaWithSupportedBrowser =
+ atKey === 'nvda' && (browserKey === 'chrome' || browserKey === 'firefox');
+ const isVoiceOverMacWithSafari =
+ atKey === 'voiceover_macos' && browserKey === 'safari_macos';
+
+ if (!isNvdaWithSupportedBrowser && !isVoiceOverMacWithSafari) {
+ return false;
+ }
+
+ // If there are version requirements, check if they are supported by automation
+ if (ctx.minimumAtVersion || ctx.exactAtVersion) {
+ return atVersionRequirementsSupportedByAutomation(ctx);
+ }
+ return true;
+};
+
+/**
+ * Checks if the version requirements are supported by automation
+ * @param {object} ctx
+ * @param {object} ctx.at
+ * @param {object} ctx.minimumAtVersion
+ * @param {object} ctx.exactAtVersion
+ * @returns {boolean}
+ */
+const atVersionRequirementsSupportedByAutomation = ({
+ at,
+ minimumAtVersion,
+ exactAtVersion
+}) => {
+ if (!at || !(exactAtVersion || minimumAtVersion)) {
+ return false;
+ }
+
+ if (minimumAtVersion && exactAtVersion) {
+ console.warn(
+ 'Both minimumAtVersion and exactAtVersion are set. This is invalid. Using exactAtVersion'
+ );
+ minimumAtVersion = null;
+ }
+
+ const { atVersions } = at;
+ if (!atVersions) {
+ console.warn(
+ 'Version requirements check was done without atVersions in the supplied at object'
+ );
+ return false;
+ }
+
+ if (exactAtVersion) {
+ return exactAtVersion.supportedByAutomation;
+ } else {
+ return atVersions.some(
+ version =>
+ version.supportedByAutomation &&
+ new Date(version.releasedAt) >= new Date(minimumAtVersion.releasedAt)
);
+ }
};
/**
@@ -20,13 +83,13 @@ export const isSupportedByResponseCollector = ctx => {
* @returns {string|undefined}
*/
export const getBotUsernameFromAtBrowser = (at, browser) => {
- if (
- at?.key === 'nvda' &&
- (browser?.key === 'chrome' || browser?.key === 'firefox')
- ) {
- return 'NVDA Bot';
- }
- if (at?.key === 'voiceover_macos' && browser?.key === 'safari_macos') {
- return 'VoiceOver Bot';
- }
+ if (
+ at?.key === 'nvda' &&
+ (browser?.key === 'chrome' || browser?.key === 'firefox')
+ ) {
+ return 'NVDA Bot';
+ }
+ if (at?.key === 'voiceover_macos' && browser?.key === 'safari_macos') {
+ return 'VoiceOver Bot';
+ }
};
diff --git a/client/utils/calculatePercentComplete.js b/client/utils/calculatePercentComplete.js
index f437e0a3c..941c19a55 100644
--- a/client/utils/calculatePercentComplete.js
+++ b/client/utils/calculatePercentComplete.js
@@ -1,14 +1,14 @@
export const calculatePercentComplete = ({ metrics, draftTestPlanRuns }) => {
- if (!metrics || !draftTestPlanRuns) return 0;
- const assignedUserCount = draftTestPlanRuns.length || 1;
- const totalTestsPossible = metrics.testsCount * assignedUserCount;
- let totalTestsCompleted = 0;
- draftTestPlanRuns.forEach(draftTestPlanRun => {
- totalTestsCompleted += draftTestPlanRun.testResults.filter(
- ({ completedAt }) => !!completedAt
- ).length;
- });
- const percentage = (totalTestsCompleted / totalTestsPossible) * 100;
- if (isNaN(percentage) || !isFinite(percentage)) return 0;
- return Math.floor(percentage);
+ if (!metrics || !draftTestPlanRuns) return 0;
+ const assignedUserCount = draftTestPlanRuns.length || 1;
+ const totalTestsPossible = metrics.testsCount * assignedUserCount;
+ let totalTestsCompleted = 0;
+ draftTestPlanRuns.forEach(draftTestPlanRun => {
+ totalTestsCompleted += draftTestPlanRun.testResults.filter(
+ ({ completedAt }) => !!completedAt
+ ).length;
+ });
+ const percentage = (totalTestsCompleted / totalTestsPossible) * 100;
+ if (isNaN(percentage) || !isFinite(percentage)) return 0;
+ return Math.floor(percentage);
};
diff --git a/client/utils/collectionJobStatus.js b/client/utils/collectionJobStatus.js
new file mode 100644
index 000000000..c0a251ef0
--- /dev/null
+++ b/client/utils/collectionJobStatus.js
@@ -0,0 +1,16 @@
+// server side version of this enum.
+// don't forget to also update the client side version in
+// server/util/enums.js
+
+export const COLLECTION_JOB_STATUS = {
+ QUEUED: 'QUEUED',
+ RUNNING: 'RUNNING',
+ COMPLETED: 'COMPLETED',
+ ERROR: 'ERROR',
+ CANCELLED: 'CANCELLED'
+};
+
+export const isJobStatusFinal = status =>
+ status === COLLECTION_JOB_STATUS.COMPLETED ||
+ status === COLLECTION_JOB_STATUS.CANCELLED ||
+ status === COLLECTION_JOB_STATUS.ERROR;
diff --git a/client/utils/constants.js b/client/utils/constants.js
index 8f496c7cf..95ac65805 100644
--- a/client/utils/constants.js
+++ b/client/utils/constants.js
@@ -1,6 +1,6 @@
export const TEST_PLAN_VERSION_PHASES = {
- RD: 'RD',
- DRAFT: 'DRAFT',
- CANDIDATE: 'CANDIDATE',
- RECOMMENDED: 'RECOMMENDED'
+ RD: 'RD',
+ DRAFT: 'DRAFT',
+ CANDIDATE: 'CANDIDATE',
+ RECOMMENDED: 'RECOMMENDED'
};
diff --git a/client/utils/createIssueLink.js b/client/utils/createIssueLink.js
index 4085b398f..a2bd73d10 100644
--- a/client/utils/createIssueLink.js
+++ b/client/utils/createIssueLink.js
@@ -1,12 +1,12 @@
const GITHUB_ISSUES_URL =
- process.env.ENVIRONMENT === 'production'
- ? 'https://github.com/w3c/aria-at'
- : 'https://github.com/bocoup/aria-at';
+ process.env.ENVIRONMENT === 'production'
+ ? 'https://github.com/w3c/aria-at'
+ : 'https://github.com/bocoup/aria-at';
const atLabelMap = {
- 'VoiceOver for macOS': 'vo',
- JAWS: 'jaws',
- NVDA: 'nvda'
+ 'VoiceOver for macOS': 'vo',
+ JAWS: 'jaws',
+ NVDA: 'nvda'
};
/**
@@ -32,122 +32,122 @@ const atLabelMap = {
* @throws {Error} If required parameters are missing
*/
const createIssueLink = ({
- isCandidateReview = false,
- isCandidateReviewChangesRequested = false,
- testPlanDirectory,
- testPlanTitle,
- versionString,
- testTitle = null,
- testSequenceNumber = null,
- testRowNumber = null,
- testRenderedUrl = null,
- atName,
- atVersionName = null,
- browserName = null,
- browserVersionName = null,
- conflictMarkdown = null,
- reportLink = null
+ isCandidateReview = false,
+ isCandidateReviewChangesRequested = false,
+ testPlanDirectory,
+ testPlanTitle,
+ versionString,
+ testTitle = null,
+ testSequenceNumber = null,
+ testRowNumber = null,
+ testRenderedUrl = null,
+ atName,
+ atVersionName = null,
+ browserName = null,
+ browserVersionName = null,
+ conflictMarkdown = null,
+ reportLink = null
}) => {
- if (!(testPlanDirectory || testPlanTitle || versionString || atName)) {
- throw new Error('Cannot create issue link due to missing parameters');
- }
-
- const hasTest = !!(
- testTitle &&
- testSequenceNumber &&
- testRowNumber &&
- testRenderedUrl
- );
-
- let title;
- if (hasTest) {
- let titleStart;
- if (isCandidateReview) {
- titleStart = isCandidateReviewChangesRequested
- ? `${atName} Changes Requested`
- : `${atName} Feedback`;
- } else {
- titleStart = 'Feedback';
- }
-
- title =
- `${titleStart}: "${testTitle}" (${testPlanTitle}, ` +
- `Test ${testSequenceNumber}, ${versionString})`;
+ if (!(testPlanDirectory || testPlanTitle || versionString || atName)) {
+ throw new Error('Cannot create issue link due to missing parameters');
+ }
+
+ const hasTest = !!(
+ testTitle &&
+ testSequenceNumber &&
+ testRowNumber &&
+ testRenderedUrl
+ );
+
+ let title;
+ if (hasTest) {
+ let titleStart;
+ if (isCandidateReview) {
+ titleStart = isCandidateReviewChangesRequested
+ ? `${atName} Changes Requested`
+ : `${atName} Feedback`;
} else {
- title = `${atName} General Feedback: ${testPlanTitle} ${versionString}`;
+ titleStart = 'Feedback';
}
- const labels =
- (isCandidateReview ? 'candidate-review,' : '') +
- `${atLabelMap[atName]},` +
- (isCandidateReviewChangesRequested ? 'changes-requested' : 'feedback');
+ title =
+ `${titleStart}: "${testTitle}" (${testPlanTitle}, ` +
+ `Test ${testSequenceNumber}, ${versionString})`;
+ } else {
+ title = `${atName} General Feedback: ${testPlanTitle} ${versionString}`;
+ }
+
+ const labels =
+ (isCandidateReview ? 'candidate-review,' : '') +
+ `${atLabelMap[atName]},` +
+ (isCandidateReviewChangesRequested ? 'changes-requested' : 'feedback');
+
+ let reportLinkFormatted = '';
+ if (reportLink) {
+ reportLinkFormatted = `- Report Page: [Link](${reportLink})\n`;
+ }
+
+ let testSetupFormatted = '';
+ if (hasTest) {
+ // TODO: fix renderedUrl
+ let modifiedRenderedUrl = testRenderedUrl.replace(
+ /.+(?=\/tests)/,
+ 'https://aria-at.netlify.app'
+ );
- let reportLinkFormatted = '';
- if (reportLink) {
- reportLinkFormatted = `- Report Page: [Link](${reportLink})\n`;
- }
+ const shortenedUrl = modifiedRenderedUrl?.match(/[^/]+$/)[0];
- let testSetupFormatted = '';
- if (hasTest) {
- // TODO: fix renderedUrl
- let modifiedRenderedUrl = testRenderedUrl.replace(
- /.+(?=\/tests)/,
- 'https://aria-at.netlify.app'
- );
-
- const shortenedUrl = modifiedRenderedUrl?.match(/[^/]+$/)[0];
-
- let atFormatted;
- if (atVersionName) {
- atFormatted = `- AT: ${atName} (Version ${atVersionName})\n`;
- } else {
- atFormatted = `- AT: ${atName}\n`;
- }
-
- let browserFormatted = '';
- if (browserName && browserVersionName) {
- browserFormatted = `- Browser: ${browserName} (Version ${browserVersionName})\n`;
- } else if (browserName) {
- browserFormatted = `- Browser: ${browserName}\n`;
- }
-
- testSetupFormatted =
- `## Test Setup\n\n` +
- `- Test File: ` +
- `[${shortenedUrl}](${modifiedRenderedUrl})\n` +
- reportLinkFormatted +
- atFormatted +
- browserFormatted +
- '\n';
+ let atFormatted;
+ if (atVersionName) {
+ atFormatted = `- AT: ${atName} (Version ${atVersionName})\n`;
+ } else {
+ atFormatted = `- AT: ${atName}\n`;
}
- const hiddenIssueMetadata = JSON.stringify({
- testPlanDirectory,
- versionString,
- atName,
- browserName,
- testRowNumber,
- testSequenceNumber,
- isCandidateReview,
- isCandidateReviewChangesRequested
- });
-
- let body =
- `## Description of Behavior\n\n` +
- `\n\n` +
- testSetupFormatted +
- `\n` +
- ``;
-
- if (conflictMarkdown) {
- body += `\n${conflictMarkdown}`;
+ let browserFormatted = '';
+ if (browserName && browserVersionName) {
+ browserFormatted = `- Browser: ${browserName} (Version ${browserVersionName})\n`;
+ } else if (browserName) {
+ browserFormatted = `- Browser: ${browserName}\n`;
}
- return (
- `${GITHUB_ISSUES_URL}/issues/new?title=${encodeURI(title)}&` +
- `labels=${labels}&body=${encodeURIComponent(body)}`
- );
+ testSetupFormatted =
+ `## Test Setup\n\n` +
+ `- Test File: ` +
+ `[${shortenedUrl}](${modifiedRenderedUrl})\n` +
+ reportLinkFormatted +
+ atFormatted +
+ browserFormatted +
+ '\n';
+ }
+
+ const hiddenIssueMetadata = JSON.stringify({
+ testPlanDirectory,
+ versionString,
+ atName,
+ browserName,
+ testRowNumber,
+ testSequenceNumber,
+ isCandidateReview,
+ isCandidateReviewChangesRequested
+ });
+
+ let body =
+ `## Description of Behavior\n\n` +
+ `\n\n` +
+ testSetupFormatted +
+ `\n` +
+ ``;
+
+ if (conflictMarkdown) {
+ body += `\n${conflictMarkdown}`;
+ }
+
+ return (
+ `${GITHUB_ISSUES_URL}/issues/new?title=${encodeURI(title)}&` +
+ `labels=${labels}&body=${encodeURIComponent(body)}`
+ );
};
/**
@@ -164,37 +164,37 @@ const createIssueLink = ({
* @returns {string} The URL for searching issues on the GitHub repository
*/
export const getIssueSearchLink = ({
- isCandidateReview = false,
- isCandidateReviewChangesRequested = false,
- username = null,
- atName,
- testPlanTitle,
- versionString,
- testSequenceNumber = null
+ isCandidateReview = false,
+ isCandidateReviewChangesRequested = false,
+ username = null,
+ atName,
+ testPlanTitle,
+ versionString,
+ testSequenceNumber = null
}) => {
- let atKey;
- if (atName === 'JAWS' || atName === 'NVDA') {
- atKey = atName.toLowerCase();
- } else {
- atKey = 'vo';
- }
-
- const query = [
- isCandidateReview ? `label:candidate-review` : '',
- isCandidateReviewChangesRequested
- ? `label:changes-requested`
- : 'label:feedback',
- `label:${atLabelMap[atName]}`,
- username ? `author:${username}` : '',
- `label:${atKey}`,
- `"${testPlanTitle}"`,
- testSequenceNumber ? `Test ${testSequenceNumber}` : '',
- versionString
- ]
- .filter(str => str)
- .join(' ');
-
- return `${GITHUB_ISSUES_URL}/issues?q=${encodeURI(query)}`;
+ let atKey;
+ if (atName === 'JAWS' || atName === 'NVDA') {
+ atKey = atName.toLowerCase();
+ } else {
+ atKey = 'vo';
+ }
+
+ const query = [
+ isCandidateReview ? `label:candidate-review` : '',
+ isCandidateReviewChangesRequested
+ ? `label:changes-requested`
+ : 'label:feedback',
+ `label:${atLabelMap[atName]}`,
+ username ? `author:${username}` : '',
+ `label:${atKey}`,
+ `"${testPlanTitle}"`,
+ testSequenceNumber ? `Test ${testSequenceNumber}` : '',
+ versionString
+ ]
+ .filter(str => str)
+ .join(' ');
+
+ return `${GITHUB_ISSUES_URL}/issues?q=${encodeURI(query)}`;
};
export default createIssueLink;
diff --git a/client/utils/evaluateAuth.js b/client/utils/evaluateAuth.js
index 34fcc145a..6b1fd246b 100644
--- a/client/utils/evaluateAuth.js
+++ b/client/utils/evaluateAuth.js
@@ -10,22 +10,22 @@
* @returns {Auth} - evaluated auth object
*/
export const evaluateAuth = user => {
- if (!user) user = {};
+ if (!user) user = {};
- let roles = user.roles ?? [];
+ let roles = user.roles ?? [];
- return {
- // calculated booleans
- isAdmin: roles.includes('ADMIN'),
- isTester: roles.includes('TESTER'),
- isVendor: roles.includes('VENDOR'),
- isSignedIn: !!user.username,
+ return {
+ // calculated booleans
+ isAdmin: roles.includes('ADMIN'),
+ isTester: roles.includes('TESTER'),
+ isVendor: roles.includes('VENDOR'),
+ isSignedIn: !!user.username,
- // user object values
- id: user.id ?? null,
- username: user.username ?? null,
- roles
- };
+ // user object values
+ id: user.id ?? null,
+ username: user.username ?? null,
+ roles
+ };
};
/**
diff --git a/client/utils/formSerialization.js b/client/utils/formSerialization.js
index f2f7018a3..a8b0f1508 100644
--- a/client/utils/formSerialization.js
+++ b/client/utils/formSerialization.js
@@ -5,46 +5,46 @@ const selector = 'input, button, textarea, select, fieldset, optgroup, option';
// 'name', 'disabled', and 'selected'
// state
export function serialize(root) {
- const nodes = root.querySelectorAll(selector);
- // serialized element state is saved in an
- // array since element traversal order will
- // always be the same
- let serialized = [];
+ const nodes = root.querySelectorAll(selector);
+ // serialized element state is saved in an
+ // array since element traversal order will
+ // always be the same
+ let serialized = [];
- for (let node of nodes) {
- let nodeState = {};
+ for (let node of nodes) {
+ let nodeState = {};
- switch (node.tagName.toLowerCase()) {
- case 'input':
- if (node.type == 'checkbox' || node.type == 'radio') {
- nodeState.checked = node.checked;
- nodeState.indeterminate = node.indeterminate;
- }
- // falls through
- case 'button':
- case 'textarea':
- nodeState.value = node.value;
- // falls through
- case 'select':
- case 'fieldset':
- nodeState.name = node.name;
- // falls through
- case 'optgroup':
- nodeState.disabled = node.disabled;
- break;
- case 'option':
- nodeState.value = node.value;
- nodeState.disabled = node.disabled;
- nodeState.selected = node.selected;
- break;
- default:
- break;
+ switch (node.tagName.toLowerCase()) {
+ case 'input':
+ if (node.type == 'checkbox' || node.type == 'radio') {
+ nodeState.checked = node.checked;
+ nodeState.indeterminate = node.indeterminate;
}
-
- serialized.push(nodeState);
+ // falls through
+ case 'button':
+ case 'textarea':
+ nodeState.value = node.value;
+ // falls through
+ case 'select':
+ case 'fieldset':
+ nodeState.name = node.name;
+ // falls through
+ case 'optgroup':
+ nodeState.disabled = node.disabled;
+ break;
+ case 'option':
+ nodeState.value = node.value;
+ nodeState.disabled = node.disabled;
+ nodeState.selected = node.selected;
+ break;
+ default:
+ break;
}
- return serialized;
+ serialized.push(nodeState);
+ }
+
+ return serialized;
}
// hydrate form elements by restoring
@@ -52,32 +52,32 @@ export function serialize(root) {
// 'name', 'disabled', and 'selected'
// state
export function hydrate(serialized, root) {
- const nodes = root.querySelectorAll(selector);
+ const nodes = root.querySelectorAll(selector);
- if (nodes.length !== serialized.length)
- throw new Error(
- 'Form deserialization failed: serialized nodes are not the same shape as given nodes'
- );
+ if (nodes.length !== serialized.length)
+ throw new Error(
+ 'Form deserialization failed: serialized nodes are not the same shape as given nodes'
+ );
- for (let [i, node] of nodes.entries()) {
- const nodeState = serialized[i];
- if (typeof nodeState.disabled !== 'undefined') {
- node.disabled = nodeState.disabled;
- }
- if (typeof nodeState.name !== 'undefined') {
- node.name = nodeState.name;
- }
- if (typeof nodeState.value !== 'undefined') {
- node.value = nodeState.value;
- }
- if (typeof nodeState.selected !== 'undefined') {
- node.selected = nodeState.selected;
- }
- if (typeof nodeState.indeterminate !== 'undefined') {
- node.indeterminate = nodeState.indeterminate;
- }
- if (typeof nodeState.checked !== 'undefined') {
- node.checked = nodeState.checked;
- }
+ for (let [i, node] of nodes.entries()) {
+ const nodeState = serialized[i];
+ if (typeof nodeState.disabled !== 'undefined') {
+ node.disabled = nodeState.disabled;
+ }
+ if (typeof nodeState.name !== 'undefined') {
+ node.name = nodeState.name;
+ }
+ if (typeof nodeState.value !== 'undefined') {
+ node.value = nodeState.value;
+ }
+ if (typeof nodeState.selected !== 'undefined') {
+ node.selected = nodeState.selected;
+ }
+ if (typeof nodeState.indeterminate !== 'undefined') {
+ node.indeterminate = nodeState.indeterminate;
+ }
+ if (typeof nodeState.checked !== 'undefined') {
+ node.checked = nodeState.checked;
}
+ }
}
diff --git a/client/utils/formatter.js b/client/utils/formatter.js
index 7951603bc..c5f4487e7 100644
--- a/client/utils/formatter.js
+++ b/client/utils/formatter.js
@@ -12,40 +12,40 @@ import moment from 'moment';
* @returns {string} - transformed string
*/
export const capitalizeEachWord = (
- sentence,
- { splitChar = ' ', joinChar = ' ' }
+ sentence,
+ { splitChar = ' ', joinChar = ' ' }
) => {
- const words = sentence.toLowerCase().split(splitChar);
- for (let i = 0; i < words.length; i++)
- words[i] = `${words[i][0].toUpperCase() + words[i].substr(1)}`;
+ const words = sentence.toLowerCase().split(splitChar);
+ for (let i = 0; i < words.length; i++)
+ words[i] = `${words[i][0].toUpperCase() + words[i].substr(1)}`;
- return words.join(joinChar);
+ return words.join(joinChar);
};
export const convertDateToString = (date, format = 'DD-MM-YYYY') => {
- if (!date) return '';
- return moment(date).format(format);
+ if (!date) return '';
+ return moment(date).format(format);
};
export const convertStringToDate = (date, format = 'DD-MM-YYYY') => {
- return moment(date, format).toDate();
+ return moment(date, format).toDate();
};
export const convertStringFormatToAnotherFormat = (
- date,
- fromFormat = 'DD-MM-YYYY',
- toFormat = 'MM-DD-YYYY'
+ date,
+ fromFormat = 'DD-MM-YYYY',
+ toFormat = 'MM-DD-YYYY'
) => {
- return moment(date, fromFormat).format(toFormat);
+ return moment(date, fromFormat).format(toFormat);
};
export const isValidDate = (date, format = 'DD-MM-YYYY') => {
- return moment(date, format).isValid();
+ return moment(date, format).isValid();
};
export const checkDaysBetweenDates = (date, otherDate) => {
- const _date = moment(date);
- const _otherDate = moment(otherDate);
- const hours = _date.diff(_otherDate, 'hours');
- return Math.ceil(hours / 24);
+ const _date = moment(date);
+ const _otherDate = moment(otherDate);
+ const hours = _date.diff(_otherDate, 'hours');
+ return Math.ceil(hours / 24);
};
diff --git a/client/utils/gitUtils.js b/client/utils/gitUtils.js
index 4de5da85d..d3ad2cd04 100644
--- a/client/utils/gitUtils.js
+++ b/client/utils/gitUtils.js
@@ -3,15 +3,15 @@ const timeZone = 'UTC';
const options = { month: 'short' };
export const gitUpdatedDateToString = (dateString, locale = 'default') => {
- const date = new Date(dateString);
- const month = date.toLocaleString(locale, options);
- const day = date.getDate();
- const year = date.getFullYear();
- const time = date
- .toLocaleTimeString(locale, { timeZone: timeZone })
- .replace(/\s/g, ' ');
+ const date = new Date(dateString);
+ const month = date.toLocaleString(locale, options);
+ const day = date.getDate();
+ const year = date.getFullYear();
+ const time = date
+ .toLocaleTimeString(locale, { timeZone: timeZone })
+ .replace(/\s/g, ' ');
- const timeStamp = `${month} ${day}, ${year} at ${time} ${timeZone}`;
+ const timeStamp = `${month} ${day}, ${year} at ${time} ${timeZone}`;
- return lc('PM', lc('AM', timeStamp));
+ return lc('PM', lc('AM', timeStamp));
};
diff --git a/client/utils/gitUtils.test.js b/client/utils/gitUtils.test.js
index e6d7d480d..793e255de 100644
--- a/client/utils/gitUtils.test.js
+++ b/client/utils/gitUtils.test.js
@@ -1,28 +1,28 @@
import { gitUpdatedDateToString } from './gitUtils';
describe('gitUpdatedDateToString', () => {
- it('returns a formatted string AM', () => {
- const date = '2021-11-30T09:51:28.000Z';
- const formattedDate = gitUpdatedDateToString(date, 'en-US');
- expect(formattedDate).toBe('Nov 30, 2021 at 9:51:28 am UTC');
- });
+ it('returns a formatted string AM', () => {
+ const date = '2021-11-30T09:51:28.000Z';
+ const formattedDate = gitUpdatedDateToString(date, 'en-US');
+ expect(formattedDate).toBe('Nov 30, 2021 at 9:51:28 am UTC');
+ });
- it('returns a formatted string PM', () => {
- const date = '2021-11-30T14:51:28.000Z';
- const formattedDate = gitUpdatedDateToString(date, 'en-US');
- expect(formattedDate).toBe('Nov 30, 2021 at 2:51:28 pm UTC');
- });
+ it('returns a formatted string PM', () => {
+ const date = '2021-11-30T14:51:28.000Z';
+ const formattedDate = gitUpdatedDateToString(date, 'en-US');
+ expect(formattedDate).toBe('Nov 30, 2021 at 2:51:28 pm UTC');
+ });
- it('returns a formatted string when using 24-hour notation in a different locale (French)', () => {
- const date = '2021-11-30T14:51:28.000Z';
- const formattedDate = gitUpdatedDateToString(date, 'fr-FR');
- // French time uses a lowercase month notation
- expect(formattedDate).toBe('nov. 30, 2021 at 14:51:28 UTC');
- });
+ it('returns a formatted string when using 24-hour notation in a different locale (French)', () => {
+ const date = '2021-11-30T14:51:28.000Z';
+ const formattedDate = gitUpdatedDateToString(date, 'fr-FR');
+ // French time uses a lowercase month notation
+ expect(formattedDate).toBe('nov. 30, 2021 at 14:51:28 UTC');
+ });
- it('returns a formatted string when using a different locale (Korean)', () => {
- const date = '2021-11-30T14:51:28.000Z';
- const formattedDate = gitUpdatedDateToString(date, 'ko-KR');
- expect(formattedDate).toBe('11월 30, 2021 at 오후 2:51:28 UTC');
- });
+ it('returns a formatted string when using a different locale (Korean)', () => {
+ const date = '2021-11-30T14:51:28.000Z';
+ const formattedDate = gitUpdatedDateToString(date, 'ko-KR');
+ expect(formattedDate).toBe('11월 30, 2021 at 오후 2:51:28 UTC');
+ });
});
diff --git a/client/utils/navigateTests.js b/client/utils/navigateTests.js
index 8bc4ea6ba..7c8346f16 100644
--- a/client/utils/navigateTests.js
+++ b/client/utils/navigateTests.js
@@ -1,32 +1,31 @@
export const navigateTests = (
- previous = false,
- currentTest,
- tests = [],
- setCurrentTestIndex = () => {},
- setIsFirstTest = () => {},
- setIsLastTest = () => {}
+ previous = false,
+ currentTest,
+ tests = [],
+ setCurrentTestIndex = () => {},
+ setIsFirstTest = () => {},
+ setIsLastTest = () => {}
) => {
- // assume navigation forward if previous is false
- let newTestIndex = currentTest.seq;
- if (!previous) {
- // next
- const newTestIndexToEval = currentTest.seq + 1;
- if (newTestIndexToEval <= tests.length)
- newTestIndex = newTestIndexToEval;
- } else {
- // previous
- const newTestIndexToEval = currentTest.seq - 1;
- if (newTestIndexToEval >= 1 && newTestIndexToEval <= tests.length)
- newTestIndex = newTestIndexToEval;
- }
+ // assume navigation forward if previous is false
+ let newTestIndex = currentTest.seq;
+ if (!previous) {
+ // next
+ const newTestIndexToEval = currentTest.seq + 1;
+ if (newTestIndexToEval <= tests.length) newTestIndex = newTestIndexToEval;
+ } else {
+ // previous
+ const newTestIndexToEval = currentTest.seq - 1;
+ if (newTestIndexToEval >= 1 && newTestIndexToEval <= tests.length)
+ newTestIndex = newTestIndexToEval;
+ }
- const currentIndex = tests.find(t => t.seq === newTestIndex).index;
- const isFirstTest = newTestIndex - 1 === 0;
- const isLastTest = newTestIndex === tests.length;
+ const currentIndex = tests.find(t => t.seq === newTestIndex).index;
+ const isFirstTest = newTestIndex - 1 === 0;
+ const isLastTest = newTestIndex === tests.length;
- setCurrentTestIndex && setCurrentTestIndex(currentIndex);
- setIsFirstTest && setIsFirstTest(isFirstTest);
- setIsLastTest && setIsLastTest(isLastTest);
+ setCurrentTestIndex && setCurrentTestIndex(currentIndex);
+ setIsFirstTest && setIsFirstTest(isFirstTest);
+ setIsLastTest && setIsLastTest(isLastTest);
- return { currentIndex, isFirstTest, isLastTest };
+ return { currentIndex, isFirstTest, isLastTest };
};
diff --git a/client/webpack.config.js b/client/webpack.config.js
index b74fa1861..a75d50806 100644
--- a/client/webpack.config.js
+++ b/client/webpack.config.js
@@ -4,92 +4,92 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
require('dotenv').config({ path: '../config/dev.env' });
module.exports = {
- entry: ['babel-polyfill', './index.js'],
- mode: 'development',
- devtool: 'inline-source-map',
- module: {
- rules: [
- {
- test: /\.(js|jsx)$/,
- exclude: /(node_modules)/,
- loader: 'babel-loader',
- options: {
- babelrcRoots: ['.', '../..']
- }
- },
- {
- test: /\.css$/,
- use: ['style-loader', 'css-loader']
- },
- {
- test: /\.s[ac]ss$/i,
- use: [
- // Creates `style` nodes from JS strings
- 'style-loader',
- // Translates CSS into CommonJS
- 'css-loader'
- ]
- },
- {
- test: /\.(png|jpe?g|gif)$/i,
- use: [
- {
- loader: 'file-loader'
- }
- ]
- }
+ entry: ['babel-polyfill', './index.js'],
+ mode: 'development',
+ devtool: 'inline-source-map',
+ module: {
+ rules: [
+ {
+ test: /\.(js|jsx)$/,
+ exclude: /(node_modules)/,
+ loader: 'babel-loader',
+ options: {
+ babelrcRoots: ['.', '../..']
+ }
+ },
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader']
+ },
+ {
+ test: /\.s[ac]ss$/i,
+ use: [
+ // Creates `style` nodes from JS strings
+ 'style-loader',
+ // Translates CSS into CommonJS
+ 'css-loader'
]
- },
- output: {
- path: path.resolve(__dirname, './dist/'),
- filename: 'bundle.js'
- },
- resolve: {
- alias: {
- '@': path.resolve(__dirname, '..'),
- '@client': __dirname,
- '@components': path.resolve(__dirname, 'components'),
- '@server': path.resolve(__dirname, '../server')
- },
- extensions: ['*', '.js', '.jsx']
- },
- devServer: {
- static: {
- directory: path.join(__dirname, 'static')
- },
- port: process.env.CLIENT_PORT || 3000,
- // Allows access to the dev server over your local network. Note that
- // you will need to use your computer's address, e.g. 192.168.0.20:3000,
- // and that logging in will require you to manually change the URL from
- // localhost:3000 to 192.168.0.20:3000 each time a redirect occurs.
- host: '0.0.0.0',
- devMiddleware: {
- publicPath: '/'
- },
- historyApiFallback: true,
- hot: 'only',
- proxy: [
- {
- context: ['/aria-at', '/api', '/embed'],
- target: `http://localhost:${process.env.PORT || 8000}`
- }
+ },
+ {
+ test: /\.(png|jpe?g|gif)$/i,
+ use: [
+ {
+ loader: 'file-loader'
+ }
]
+ }
+ ]
+ },
+ output: {
+ path: path.resolve(__dirname, './dist/'),
+ filename: 'bundle.js'
+ },
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, '..'),
+ '@client': __dirname,
+ '@components': path.resolve(__dirname, 'components'),
+ '@server': path.resolve(__dirname, '../server')
+ },
+ extensions: ['*', '.js', '.jsx']
+ },
+ devServer: {
+ static: {
+ directory: path.join(__dirname, 'static')
},
- watchOptions: {
- aggregateTimeout: 300,
- poll: 1000
+ port: process.env.CLIENT_PORT || 3000,
+ // Allows access to the dev server over your local network. Note that
+ // you will need to use your computer's address, e.g. 192.168.0.20:3000,
+ // and that logging in will require you to manually change the URL from
+ // localhost:3000 to 192.168.0.20:3000 each time a redirect occurs.
+ host: '0.0.0.0',
+ devMiddleware: {
+ publicPath: '/'
},
- plugins: [
- new CopyWebpackPlugin({
- patterns: [
- {
- from: 'static'
- }
- ]
- }),
- new webpack.DefinePlugin({
- 'process.env.API_SERVER': JSON.stringify(process.env.API_SERVER),
- 'process.env.ENVIRONMENT': JSON.stringify(process.env.ENVIRONMENT)
- })
+ historyApiFallback: true,
+ hot: 'only',
+ proxy: [
+ {
+ context: ['/aria-at', '/api', '/embed'],
+ target: `http://localhost:${process.env.PORT || 8000}`
+ }
]
+ },
+ watchOptions: {
+ aggregateTimeout: 300,
+ poll: 1000
+ },
+ plugins: [
+ new CopyWebpackPlugin({
+ patterns: [
+ {
+ from: 'static'
+ }
+ ]
+ }),
+ new webpack.DefinePlugin({
+ 'process.env.API_SERVER': JSON.stringify(process.env.API_SERVER),
+ 'process.env.ENVIRONMENT': JSON.stringify(process.env.ENVIRONMENT)
+ })
+ ]
};
diff --git a/client/webpack.prod.js b/client/webpack.prod.js
index 61ebb6124..06d73b603 100644
--- a/client/webpack.prod.js
+++ b/client/webpack.prod.js
@@ -3,70 +3,70 @@ const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
- entry: ['babel-polyfill', './index.js'],
- mode: 'production',
- module: {
- rules: [
- {
- test: /\.(js|jsx)$/,
- exclude: /(node_modules)/,
- loader: 'babel-loader',
- options: {
- babelrcRoots: ['.', '../..']
- }
- },
- {
- test: /\.css$/,
- use: ['style-loader', 'css-loader']
- },
- {
- test: /\.s[ac]ss$/i,
- use: [
- // Creates `style` nodes from JS strings
- 'style-loader',
- // Translates CSS into CommonJS
- 'css-loader'
- ]
- },
- {
- test: /\.(png|jpe?g|gif)$/i,
- use: [
- {
- loader: 'file-loader'
- }
- ]
- }
+ entry: ['babel-polyfill', './index.js'],
+ mode: 'production',
+ module: {
+ rules: [
+ {
+ test: /\.(js|jsx)$/,
+ exclude: /(node_modules)/,
+ loader: 'babel-loader',
+ options: {
+ babelrcRoots: ['.', '../..']
+ }
+ },
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader']
+ },
+ {
+ test: /\.s[ac]ss$/i,
+ use: [
+ // Creates `style` nodes from JS strings
+ 'style-loader',
+ // Translates CSS into CommonJS
+ 'css-loader'
]
+ },
+ {
+ test: /\.(png|jpe?g|gif)$/i,
+ use: [
+ {
+ loader: 'file-loader'
+ }
+ ]
+ }
+ ]
+ },
+ output: {
+ path: path.resolve(__dirname, './dist/'),
+ filename: 'bundle.js'
+ },
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, '..'),
+ '@client': __dirname,
+ '@components': path.resolve(__dirname, 'components'),
+ '@server': path.resolve(__dirname, '../server')
},
- output: {
- path: path.resolve(__dirname, './dist/'),
- filename: 'bundle.js'
- },
- resolve: {
- alias: {
- '@': path.resolve(__dirname, '..'),
- '@client': __dirname,
- '@components': path.resolve(__dirname, 'components'),
- '@server': path.resolve(__dirname, '../server')
- },
- extensions: ['*', '.js', '.jsx']
- },
- plugins: [
- new CopyWebpackPlugin({
- patterns: [
- {
- from: 'static'
- }
- ]
- }),
- new webpack.DefinePlugin({
- 'process.env.API_SERVER': JSON.stringify(process.env.API_SERVER),
- 'process.env.ENVIRONMENT': JSON.stringify(process.env.ENVIRONMENT)
- })
- ],
- performance: {
- hints: false,
- maxEntrypointSize: 512000,
- maxAssetSize: 512000
- }
+ extensions: ['*', '.js', '.jsx']
+ },
+ plugins: [
+ new CopyWebpackPlugin({
+ patterns: [
+ {
+ from: 'static'
+ }
+ ]
+ }),
+ new webpack.DefinePlugin({
+ 'process.env.API_SERVER': JSON.stringify(process.env.API_SERVER),
+ 'process.env.ENVIRONMENT': JSON.stringify(process.env.ENVIRONMENT)
+ })
+ ],
+ performance: {
+ hints: false,
+ maxEntrypointSize: 512000,
+ maxAssetSize: 512000
+ }
};
diff --git a/config/config.js b/config/config.js
index f32cfdff0..9f2f10954 100644
--- a/config/config.js
+++ b/config/config.js
@@ -1,17 +1,17 @@
module.exports = {
- development: {
- database: process.env.PGDATABASE,
- username: process.env.PGUSER,
- password: process.env.PGPASSWORD,
- host: process.env.PGHOST,
- port: process.env.PGPORT,
- dialect: 'postgres',
- dialectOption: {
- ssl: true,
- native: true,
- multipleStatements: true
- },
- seederStorage: 'sequelize',
- logging: false // console.log // eslint-disable-line no-console
- }
+ development: {
+ database: process.env.PGDATABASE,
+ username: process.env.PGUSER,
+ password: process.env.PGPASSWORD,
+ host: process.env.PGHOST,
+ port: process.env.PGPORT,
+ dialect: 'postgres',
+ dialectOption: {
+ ssl: true,
+ native: true,
+ multipleStatements: true
+ },
+ seederStorage: 'sequelize',
+ logging: false // console.log // eslint-disable-line no-console
+ }
};
diff --git a/index.js b/index.js
index 4aeae28bf..afe12585d 100644
--- a/index.js
+++ b/index.js
@@ -8,5 +8,5 @@ const { listener } = require('./server/server');
listener.use(history()).use(express.static('./client/dist'));
listener.get('*', (req, res) => {
- res.sendFile(path.join(__dirname, './client/dist/index.html'));
+ res.sendFile(path.join(__dirname, './client/dist/index.html'));
});
diff --git a/package.json b/package.json
index 430f2a578..01312348a 100644
--- a/package.json
+++ b/package.json
@@ -33,15 +33,15 @@
"url": "https://github.com/w3c/aria-at-app/issues"
},
"homepage": "https://github.com/w3c/aria-at-app#readme",
- "dependencies": {
- "patch-package": "^6.5.1",
- "postinstall-postinstall": "^2.1.0"
- },
"workspaces": [
"client",
"server",
"shared"
],
+ "dependencies": {
+ "patch-package": "^8.0.0",
+ "postinstall-postinstall": "^2.1.0"
+ },
"devDependencies": {
"npm-run-all": "^4.1.5"
}
diff --git a/server/app.js b/server/app.js
index ff96b3899..90b3217b5 100644
--- a/server/app.js
+++ b/server/app.js
@@ -10,7 +10,9 @@ const transactionRoutes = require('./routes/transactions');
const automationSchedulerRoutes = require('./routes/automation');
const path = require('path');
const apolloServer = require('./graphql-server');
-const setupMockAutomationSchedulerServer = require('./tests/util/mock-automation-scheduler-server');
+const {
+ setupMockAutomationSchedulerServer
+} = require('./tests/util/mock-automation-scheduler-server');
const transactionMiddleware = require('./middleware/transactionMiddleware');
const app = express();
@@ -24,7 +26,7 @@ app.use('/transactions', transactionRoutes);
app.use('/jobs', automationSchedulerRoutes);
apolloServer.start().then(() => {
- apolloServer.applyMiddleware({ app });
+ apolloServer.applyMiddleware({ app });
});
const listener = express();
@@ -34,35 +36,35 @@ const baseUrl = 'https://raw.githubusercontent.com';
const onlyStatus200 = (req, res) => res.statusCode === 200;
listener.route('/aria-at/:branch*').get(
- cacheMiddleware('7 days', onlyStatus200),
- (req, res, next) => {
- req.url = path.join('w3c', req.url);
- next();
- },
- proxyMiddleware.fileRedirect(baseUrl),
- proxyMiddleware.proxyPath(baseUrl)
+ cacheMiddleware('7 days', onlyStatus200),
+ (req, res, next) => {
+ req.url = path.join('w3c', req.url);
+ next();
+ },
+ proxyMiddleware.fileRedirect(baseUrl),
+ proxyMiddleware.proxyPath(baseUrl)
);
// Conditionally initialize github workflow service, or mock automation scheduler
if (
- process.env.ENVIRONMENT === 'production' ||
- process.env.ENVIRONMENT === 'staging' ||
- process.env.ENVIRONMENT === 'sandbox' ||
- process.env.AUTOMATION_CALLBACK_FQDN
+ process.env.ENVIRONMENT === 'production' ||
+ process.env.ENVIRONMENT === 'staging' ||
+ process.env.ENVIRONMENT === 'sandbox' ||
+ process.env.AUTOMATION_CALLBACK_FQDN
) {
- require('./services/GithubWorkflowService').setup();
+ require('./services/GithubWorkflowService').setup();
} else {
- setupMockAutomationSchedulerServer().catch(error => {
- console.error('Failed to initialize mock automation server:', error);
- });
+ setupMockAutomationSchedulerServer().catch(error => {
+ console.error('Failed to initialize mock automation server:', error);
+ });
}
app.use(transactionMiddleware.errorware);
// Error handling must be the last middleware
listener.use((error, req, res, next) => {
- console.error(error);
- next(error);
+ console.error(error);
+ next(error);
});
module.exports = { app, listener };
diff --git a/server/apps/embed.js b/server/apps/embed.js
index bb1f34c11..fafd3821c 100644
--- a/server/apps/embed.js
+++ b/server/apps/embed.js
@@ -10,10 +10,10 @@ const app = express();
const handlebarsPath = path.resolve(__dirname, '../handlebars/embed');
const hbs = create({
- layoutsDir: path.resolve(handlebarsPath, 'views/layouts'),
- extname: 'hbs',
- defaultLayout: 'index',
- helpers: require(path.resolve(handlebarsPath, 'helpers'))
+ layoutsDir: path.resolve(handlebarsPath, 'views/layouts'),
+ extname: 'hbs',
+ defaultLayout: 'index',
+ helpers: require(path.resolve(handlebarsPath, 'helpers'))
});
app.engine('hbs', hbs.engine);
@@ -29,121 +29,121 @@ app.set('views', path.resolve(handlebarsPath, 'views'));
const millisecondsUntilStale = 5000;
const renderEmbed = async ({
- queryTitle,
- testPlanDirectory,
- protocol,
- host
+ queryTitle,
+ testPlanDirectory,
+ protocol,
+ host
}) => {
- const { data, errors } = await apolloServer.executeOperation({
- query: gql`
- query TestPlanQuery($testPlanDirectory: ID!) {
- ats {
- id
- name
- browsers {
- id
- name
- }
- }
- testPlan(id: $testPlanDirectory) {
- testPlanVersions {
- id
- title
- phase
- testPlanReports(isFinal: true) {
- id
- metrics
- at {
- id
- name
- }
- browser {
- id
- name
- }
- latestAtVersionReleasedAt {
- id
- name
- releasedAt
- }
- }
- }
- }
- }
- `,
- variables: { testPlanDirectory }
- });
-
- if (errors) {
- throw new Error(errors);
- }
-
- let testPlanVersion;
-
- const recommendedTestPlanVersion = data.testPlan?.testPlanVersions.find(
- testPlanVersion => testPlanVersion.phase === 'RECOMMENDED'
- );
-
- if (data.testPlan && recommendedTestPlanVersion) {
- testPlanVersion = recommendedTestPlanVersion;
- } else if (data.testPlan) {
- testPlanVersion = data.testPlan.testPlanVersions.find(
- testPlanVersion => testPlanVersion.phase === 'CANDIDATE'
- );
- }
-
- const testPlanReports = (testPlanVersion?.testPlanReports ?? []).sort(
- (a, b) => {
- if (a.at.name !== b.at.name) {
- return a.at.name.localeCompare(b.at.name);
+ const { data, errors } = await apolloServer.executeOperation({
+ query: gql`
+ query TestPlanQuery($testPlanDirectory: ID!) {
+ ats {
+ id
+ name
+ browsers {
+ id
+ name
+ }
+ }
+ testPlan(id: $testPlanDirectory) {
+ testPlanVersions {
+ id
+ title
+ phase
+ testPlanReports(isFinal: true) {
+ id
+ metrics
+ at {
+ id
+ name
+ }
+ browser {
+ id
+ name
+ }
+ latestAtVersionReleasedAt {
+ id
+ name
+ releasedAt
+ }
}
- return a.browser.name.localeCompare(b.browser.name);
+ }
}
+ }
+ `,
+ variables: { testPlanDirectory }
+ });
+
+ if (errors) {
+ throw new Error(errors);
+ }
+
+ let testPlanVersion;
+
+ const recommendedTestPlanVersion = data.testPlan?.testPlanVersions.find(
+ testPlanVersion => testPlanVersion.phase === 'RECOMMENDED'
+ );
+
+ if (data.testPlan && recommendedTestPlanVersion) {
+ testPlanVersion = recommendedTestPlanVersion;
+ } else if (data.testPlan) {
+ testPlanVersion = data.testPlan.testPlanVersions.find(
+ testPlanVersion => testPlanVersion.phase === 'CANDIDATE'
);
-
- return hbs.renderView(path.resolve(handlebarsPath, 'views/main.hbs'), {
- layout: 'index',
- dataEmpty: !testPlanVersion?.testPlanReports.length,
- title: queryTitle || testPlanVersion?.title || 'Pattern Not Found',
- phase: testPlanVersion?.phase,
- testPlanVersionId: testPlanVersion?.id,
- testPlanReports,
- protocol,
- host,
- completeReportLink: `${protocol}${host}/report/${testPlanVersion?.id}`,
- embedLink: `${protocol}${host}/embed/reports/${testPlanDirectory}`
- });
+ }
+
+ const testPlanReports = (testPlanVersion?.testPlanReports ?? []).sort(
+ (a, b) => {
+ if (a.at.name !== b.at.name) {
+ return a.at.name.localeCompare(b.at.name);
+ }
+ return a.browser.name.localeCompare(b.browser.name);
+ }
+ );
+
+ return hbs.renderView(path.resolve(handlebarsPath, 'views/main.hbs'), {
+ layout: 'index',
+ dataEmpty: !testPlanVersion?.testPlanReports.length,
+ title: queryTitle || testPlanVersion?.title || 'Pattern Not Found',
+ phase: testPlanVersion?.phase,
+ testPlanVersionId: testPlanVersion?.id,
+ testPlanReports,
+ protocol,
+ host,
+ completeReportLink: `${protocol}${host}/report/${testPlanVersion?.id}`,
+ embedLink: `${protocol}${host}/embed/reports/${testPlanDirectory}`
+ });
};
// staleWhileRevalidate() caching allows this page to handle very high traffic like
// it will see on the APG website. It works by immediately serving a recent
// version of the page and checks for updates in the background.
const renderEmbedCached = staleWhileRevalidate(renderEmbed, {
- getCacheKeyFromArguments: ({ testPlanDirectory }) => testPlanDirectory,
- millisecondsUntilStale
+ getCacheKeyFromArguments: ({ testPlanDirectory }) => testPlanDirectory,
+ millisecondsUntilStale
});
app.get('/reports/:testPlanDirectory', async (req, res) => {
- // In the instance where an editor doesn't want to display a certain title
- // as it has defined when importing into the ARIA-AT database for being too
- // verbose, etc. eg. `Link Example 1 (span element with text content)`
- // Usage: https://aria-at.w3.org/embed/reports/command-button?title=Link+Example+(span+element+with+text+content)
- const queryTitle = req.query.title;
- const testPlanDirectory = req.params.testPlanDirectory;
- const host = req.headers.host;
- const protocol = /dev|vagrant/.test(process.env.ENVIRONMENT)
- ? 'http://'
- : 'https://';
- const embedRendered = await renderEmbedCached({
- queryTitle,
- testPlanDirectory,
- protocol,
- host
- });
-
- // Disable browser-based caching which could potentially make the embed
- // contents appear stale even after being refreshed
- res.set('cache-control', 'must-revalidate').send(embedRendered);
+ // In the instance where an editor doesn't want to display a certain title
+ // as it has defined when importing into the ARIA-AT database for being too
+ // verbose, etc. eg. `Link Example 1 (span element with text content)`
+ // Usage: https://aria-at.w3.org/embed/reports/command-button?title=Link+Example+(span+element+with+text+content)
+ const queryTitle = req.query.title;
+ const testPlanDirectory = req.params.testPlanDirectory;
+ const host = req.headers.host;
+ const protocol = /dev|vagrant/.test(process.env.ENVIRONMENT)
+ ? 'http://'
+ : 'https://';
+ const embedRendered = await renderEmbedCached({
+ queryTitle,
+ testPlanDirectory,
+ protocol,
+ host
+ });
+
+ // Disable browser-based caching which could potentially make the embed
+ // contents appear stale even after being refreshed
+ res.set('cache-control', 'must-revalidate').send(embedRendered);
});
app.use(express.static(path.resolve(`${handlebarsPath}/public`)));
diff --git a/server/controllers/AuthController.js b/server/controllers/AuthController.js
index c1076ebb1..18f36508a 100644
--- a/server/controllers/AuthController.js
+++ b/server/controllers/AuthController.js
@@ -6,88 +6,88 @@ const getUsersFromFile = require('../util/getUsersFromFile');
const APP_SERVER = process.env.APP_SERVER;
const oauthRedirectToGithubController = async (req, res) => {
- const oauthUrl = GithubService.getOauthUrl();
- res.redirect(303, oauthUrl);
- res.end();
+ const oauthUrl = GithubService.getOauthUrl();
+ res.redirect(303, oauthUrl);
+ res.end();
};
const oauthRedirectFromGithubController = async (req, res) => {
- const loginSucceeded = () => {
- res.redirect(303, `${APP_SERVER}/test-queue`);
- };
- const loginFailedDueToRole = async () => {
- if (req.session) {
- await new Promise(resolve => req.session.destroy(resolve));
- }
- res.redirect(303, `${APP_SERVER}/signup-instructions`);
- };
- const loginFailedDueToGitHub = () => {
- res.status(401).send(
- `
+ const loginSucceeded = () => {
+ res.redirect(303, `${APP_SERVER}/test-queue`);
+ };
+ const loginFailedDueToRole = async () => {
+ if (req.session) {
+ await new Promise(resolve => req.session.destroy(resolve));
+ }
+ res.redirect(303, `${APP_SERVER}/signup-instructions`);
+ };
+ const loginFailedDueToGitHub = () => {
+ res.status(401).send(
+ `
ARIA-AT App failed to access GitHub.
Return to home page.
`
- );
- };
+ );
+ };
- const { code } = req.query;
+ const { code } = req.query;
- const githubAccessToken = await GithubService.getGithubAccessToken(code);
- if (!githubAccessToken) return loginFailedDueToGitHub();
+ const githubAccessToken = await GithubService.getGithubAccessToken(code);
+ if (!githubAccessToken) return loginFailedDueToGitHub();
- const githubUsername = await GithubService.getGithubUsername(
- githubAccessToken
- );
+ const githubUsername = await GithubService.getGithubUsername(
+ githubAccessToken
+ );
- if (!githubUsername) return loginFailedDueToGitHub();
+ if (!githubUsername) return loginFailedDueToGitHub();
- const admins = await getUsersFromFile('admins.txt');
- const testers = await getUsersFromFile('testers.txt');
- const vendors = await getUsersFromFile('vendors.txt');
+ const admins = await getUsersFromFile('admins.txt');
+ const testers = await getUsersFromFile('testers.txt');
+ const vendors = await getUsersFromFile('vendors.txt');
- const roles = [];
- if (admins.includes(githubUsername)) {
- roles.push({ name: User.ADMIN });
- }
- if (admins.includes(githubUsername) || testers.includes(githubUsername)) {
- roles.push({ name: User.TESTER }); // Admins are always testers
- }
+ const roles = [];
+ if (admins.includes(githubUsername)) {
+ roles.push({ name: User.ADMIN });
+ }
+ if (admins.includes(githubUsername) || testers.includes(githubUsername)) {
+ roles.push({ name: User.TESTER }); // Admins are always testers
+ }
- if (
- admins.includes(githubUsername) ||
- vendors.some(vendor => vendor.split('|')[0] === githubUsername)
- ) {
- roles.push({ name: User.VENDOR });
- }
+ if (
+ admins.includes(githubUsername) ||
+ vendors.some(vendor => vendor.split('|')[0] === githubUsername)
+ ) {
+ roles.push({ name: User.VENDOR });
+ }
- if (roles.length === 0) return loginFailedDueToRole();
+ if (roles.length === 0) return loginFailedDueToRole();
- let [user] = await getOrCreateUser({
- where: { username: githubUsername },
- values: { roles },
- atAttributes: [],
- testPlanRunAttributes: [],
- transaction: req.transaction
- });
+ let [user] = await getOrCreateUser({
+ where: { username: githubUsername },
+ values: { roles },
+ atAttributes: [],
+ testPlanRunAttributes: [],
+ transaction: req.transaction
+ });
- req.session.user = user;
+ req.session.user = user;
- return loginSucceeded();
+ return loginSucceeded();
};
const signoutController = (req, res) => {
- req.session.destroy(err => {
- if (err) {
- res.status(500);
- } else {
- res.status(200);
- }
- res.end();
- });
+ req.session.destroy(err => {
+ if (err) {
+ res.status(500);
+ } else {
+ res.status(200);
+ }
+ res.end();
+ });
};
module.exports = {
- oauthRedirectToGithubController,
- oauthRedirectFromGithubController,
- signoutController
+ oauthRedirectToGithubController,
+ oauthRedirectFromGithubController,
+ signoutController
};
diff --git a/server/controllers/AutomationController.js b/server/controllers/AutomationController.js
index bdc0fcc61..62e49a039 100644
--- a/server/controllers/AutomationController.js
+++ b/server/controllers/AutomationController.js
@@ -1,27 +1,27 @@
const axios = require('axios');
const {
- getCollectionJobById,
- updateCollectionJobById,
- updateCollectionJobTestStatusByQuery
+ getCollectionJobById,
+ updateCollectionJobById,
+ updateCollectionJobTestStatusByQuery
} = require('../models/services/CollectionJobService');
const {
- findOrCreateTestResult
+ findOrCreateTestResult
} = require('../models/services/TestResultWriteService');
const convertTestResultToInput = require('../resolvers/TestPlanRunOperations/convertTestResultToInput');
const saveTestResultCommon = require('../resolvers/TestResultOperations/saveTestResultCommon');
const {
- getAts,
- findOrCreateAtVersion
+ getAts,
+ findOrCreateAtVersion
} = require('../models/services/AtService');
const {
- getBrowsers,
- findOrCreateBrowserVersion
+ getBrowsers,
+ findOrCreateBrowserVersion
} = require('../models/services/BrowserService');
const { HttpQueryError } = require('apollo-server-core');
const { COLLECTION_JOB_STATUS, isJobStatusFinal } = require('../util/enums');
const populateData = require('../services/PopulatedData/populateData');
const {
- getFinalizedTestResults
+ getFinalizedTestResults
} = require('../models/services/TestResultReadService');
const http = require('http');
const { NO_OUTPUT_STRING } = require('../util/constants');
@@ -30,330 +30,321 @@ const getGraphQLContext = require('../graphql-context');
const httpAgent = new http.Agent({ family: 4 });
const axiosConfig = {
- headers: {
- 'x-automation-secret': process.env.AUTOMATION_SCHEDULER_SECRET
- },
- timeout: 1000,
- httpAgent
+ timeout: 1000,
+ httpAgent
};
const throwNoJobFoundError = jobId => {
- throw new HttpQueryError(
- 404,
- `Could not find job with jobId: ${jobId}`,
- true
- );
+ throw new HttpQueryError(
+ 404,
+ `Could not find job with jobId: ${jobId}`,
+ true
+ );
};
const throwNoTestFoundError = rowNumber => {
- throw new HttpQueryError(
- 404,
- `Could not find test at row number ${rowNumber}`,
- true
- );
+ throw new HttpQueryError(
+ 404,
+ `Could not find test at row number ${rowNumber}`,
+ true
+ );
};
const throwSchedulerError = schedulerResponse => {
- throw new HttpQueryError(
- 502,
- `Response scheduler did not give a correct response: ${schedulerResponse}`,
- false
- );
+ throw new HttpQueryError(
+ 502,
+ `Response scheduler did not give a correct response: ${schedulerResponse}`,
+ false
+ );
};
const cancelJob = async (req, res) => {
- const automationSchedulerResponse = await axios.post(
- `${process.env.AUTOMATION_SCHEDULER_URL}/jobs/${req.params.jobID}/cancel`,
- {},
- axiosConfig
- );
-
- if (!automationSchedulerResponse.data) {
- throwSchedulerError(automationSchedulerResponse);
- }
-
- if (
- automationSchedulerResponse.data.status ===
- COLLECTION_JOB_STATUS.CANCELLED
- ) {
- const graphqlRes = await updateCollectionJobById({
- id: req.params.jobID,
- values: { status: COLLECTION_JOB_STATUS.CANCELLED },
- transaction: req.transaction
- });
- if (!graphqlRes) {
- throwNoJobFoundError(req.params.jobID);
- }
+ const automationSchedulerResponse = await axios.post(
+ `${process.env.AUTOMATION_SCHEDULER_URL}/jobs/${req.params.jobID}/cancel`,
+ {},
+ axiosConfig
+ );
+
+ if (!automationSchedulerResponse.data) {
+ throwSchedulerError(automationSchedulerResponse);
+ }
+
+ if (
+ automationSchedulerResponse.data.status === COLLECTION_JOB_STATUS.CANCELLED
+ ) {
+ const graphqlRes = await updateCollectionJobById({
+ id: req.params.jobID,
+ values: { status: COLLECTION_JOB_STATUS.CANCELLED },
+ transaction: req.transaction
+ });
+ if (!graphqlRes) {
+ throwNoJobFoundError(req.params.jobID);
}
- res.json(automationSchedulerResponse.data);
+ }
+ res.json(automationSchedulerResponse.data);
};
const updateJobStatus = async (req, res) => {
- const { status, externalLogsUrl } = req.body;
-
- if (!Object.values(COLLECTION_JOB_STATUS).includes(status)) {
- throw new HttpQueryError(400, `Invalid status: ${status}`, true);
- }
-
- const updatePayload = {
- status,
- ...(externalLogsUrl != null && { externalLogsUrl })
- };
-
- // When new status is considered "final" ('COMPLETED' or 'ERROR' or 'CANCELLED')
- if (isJobStatusFinal(status)) {
- // update any CollectionJobTestStatus children still 'QUEUED' to be 'CANCELLED'
- await updateCollectionJobTestStatusByQuery({
- where: {
- collectionJobId: req.params.jobID,
- status: COLLECTION_JOB_STATUS.QUEUED
- },
- values: { status: COLLECTION_JOB_STATUS.CANCELLED },
- transaction: req.transaction
- });
- // update any CollectionJobTestStatus children still 'RUNNING' to be 'ERROR' or 'CANCELLED'
- let runningTestNewStatus =
- status === COLLECTION_JOB_STATUS.ERROR
- ? COLLECTION_JOB_STATUS.ERROR
- : COLLECTION_JOB_STATUS.CANCELLED;
- await updateCollectionJobTestStatusByQuery({
- where: {
- collectionJobId: req.params.jobID,
- status: COLLECTION_JOB_STATUS.RUNNING
- },
- values: { status: runningTestNewStatus },
- transaction: req.transaction
- });
- }
-
- const graphqlResponse = await updateCollectionJobById({
- id: req.params.jobID,
- values: updatePayload,
- transaction: req.transaction
+ const { status, externalLogsUrl } = req.body;
+
+ if (!Object.values(COLLECTION_JOB_STATUS).includes(status)) {
+ throw new HttpQueryError(400, `Invalid status: ${status}`, true);
+ }
+
+ const updatePayload = {
+ status,
+ ...(externalLogsUrl != null && { externalLogsUrl })
+ };
+
+ // When new status is considered "final" ('COMPLETED' or 'ERROR' or 'CANCELLED')
+ if (isJobStatusFinal(status)) {
+ // update any CollectionJobTestStatus children still 'QUEUED' to be 'CANCELLED'
+ await updateCollectionJobTestStatusByQuery({
+ where: {
+ collectionJobId: req.params.jobID,
+ status: COLLECTION_JOB_STATUS.QUEUED
+ },
+ values: { status: COLLECTION_JOB_STATUS.CANCELLED },
+ transaction: req.transaction
});
+ // update any CollectionJobTestStatus children still 'RUNNING' to be 'ERROR' or 'CANCELLED'
+ let runningTestNewStatus =
+ status === COLLECTION_JOB_STATUS.ERROR
+ ? COLLECTION_JOB_STATUS.ERROR
+ : COLLECTION_JOB_STATUS.CANCELLED;
+ await updateCollectionJobTestStatusByQuery({
+ where: {
+ collectionJobId: req.params.jobID,
+ status: COLLECTION_JOB_STATUS.RUNNING
+ },
+ values: { status: runningTestNewStatus },
+ transaction: req.transaction
+ });
+ }
- if (!graphqlResponse) {
- throwNoJobFoundError(req.params.jobID);
- }
+ const graphqlResponse = await updateCollectionJobById({
+ id: req.params.jobID,
+ values: updatePayload,
+ transaction: req.transaction
+ });
- res.json(graphqlResponse);
+ if (!graphqlResponse) {
+ throwNoJobFoundError(req.params.jobID);
+ }
+
+ res.json(graphqlResponse);
};
const getApprovedFinalizedTestResults = async (testPlanRun, context) => {
- const {
- testPlanReport: { testPlanVersion }
- } = testPlanRun;
-
- // To be considered "Approved", a test plan run must be associated with a test plan report
- // that is associated with a test plan version that is in "CANDIDATE" or "RECOMMENDED" or
- // "DRAFT" phase and the test plan report been marked as final.
- const { phase } = testPlanVersion;
-
- if (
- phase === 'RD' ||
- (phase === 'DRAFT' && testPlanRun.testPlanReport.markedFinalAt === null)
- ) {
- return null;
- }
-
- const { testPlanReport } = await populateData(
- { testPlanReportId: testPlanRun.testPlanReport.id },
- { context }
- );
-
- return getFinalizedTestResults({ testPlanReport, context });
+ const {
+ testPlanReport: { testPlanVersion }
+ } = testPlanRun;
+
+ // To be considered "Approved", a test plan run must be associated with a test plan report
+ // that is associated with a test plan version that is in "CANDIDATE" or "RECOMMENDED" or
+ // "DRAFT" phase and the test plan report been marked as final.
+ const { phase } = testPlanVersion;
+
+ if (
+ phase === 'RD' ||
+ (phase === 'DRAFT' && testPlanRun.testPlanReport.markedFinalAt === null)
+ ) {
+ return null;
+ }
+
+ const { testPlanReport } = await populateData(
+ { testPlanReportId: testPlanRun.testPlanReport.id },
+ { context }
+ );
+
+ return getFinalizedTestResults({ testPlanReport, context });
};
const getTestByRowNumber = async ({ testPlanRun, testRowNumber, context }) => {
- const tests = await runnableTestsResolver(
- testPlanRun.testPlanReport,
- null,
- context
- );
- return tests.find(test => String(test.rowNumber) === String(testRowNumber));
+ const tests = await runnableTestsResolver(
+ testPlanRun.testPlanReport,
+ null,
+ context
+ );
+ return tests.find(test => String(test.rowNumber) === String(testRowNumber));
};
const updateOrCreateTestResultWithResponses = async ({
+ testId,
+ testPlanRun,
+ responses,
+ atVersionId,
+ browserVersionId,
+ context
+}) => {
+ const { testResult } = await findOrCreateTestResult({
testId,
- testPlanRun,
- responses,
+ testPlanRunId: testPlanRun.id,
atVersionId,
browserVersionId,
context
-}) => {
- const { testResult } = await findOrCreateTestResult({
- testId,
- testPlanRunId: testPlanRun.id,
- atVersionId,
- browserVersionId,
- context
- });
+ });
- const historicalTestResults = await getApprovedFinalizedTestResults(
- testPlanRun,
- context
+ const historicalTestResults = await getApprovedFinalizedTestResults(
+ testPlanRun,
+ context
+ );
+
+ const historicalTestResult = historicalTestResults?.find(each => {
+ return each.testId === testId;
+ });
+
+ if (
+ historicalTestResult &&
+ historicalTestResult.scenarioResults?.length !==
+ testResult.scenarioResults.length
+ ) {
+ throw new Error(
+ 'Historical test result does not match current test result'
);
+ }
- const historicalTestResult = historicalTestResults?.find(each => {
- return each.testId === testId;
- });
-
- if (
+ const getAutomatedResultFromOutput = ({ baseTestResult, outputs }) => ({
+ ...baseTestResult,
+ atVersionId,
+ browserVersionId,
+ scenarioResults: baseTestResult.scenarioResults.map((scenarioResult, i) => {
+ // Check if output matches historical output
+ const outputMatches =
historicalTestResult &&
- historicalTestResult.scenarioResults?.length !==
- testResult.scenarioResults.length
- ) {
- throw new Error(
- 'Historical test result does not match current test result'
- );
- }
-
- const getAutomatedResultFromOutput = ({ baseTestResult, outputs }) => ({
- ...baseTestResult,
- atVersionId,
- browserVersionId,
- scenarioResults: baseTestResult.scenarioResults.map(
- (scenarioResult, i) => {
- // Check if output matches historical output
- const outputMatches =
- historicalTestResult &&
- historicalTestResult.scenarioResults[i] &&
- historicalTestResult.scenarioResults[i].output ===
- outputs[i];
-
- return {
- ...scenarioResult,
- output: outputs[i],
- assertionResults: scenarioResult.assertionResults.map(
- (assertionResult, j) => ({
- ...assertionResult,
- passed: outputMatches
- ? historicalTestResult.scenarioResults[i]
- .assertionResults[j].passed
- : false,
- failedReason: outputMatches
- ? historicalTestResult.scenarioResults[i]
- .assertionResults[j].failedReason
- : 'AUTOMATED_OUTPUT'
- })
- ),
- unexpectedBehaviors: null
- };
- }
- )
- });
-
- return saveTestResultCommon({
- testResultId: testResult.id,
- input: convertTestResultToInput(
- getAutomatedResultFromOutput({
- baseTestResult: testResult,
- outputs: responses
- })
+ historicalTestResult.scenarioResults[i] &&
+ historicalTestResult.scenarioResults[i].output === outputs[i];
+
+ return {
+ ...scenarioResult,
+ output: outputs[i],
+ assertionResults: scenarioResult.assertionResults.map(
+ (assertionResult, j) => ({
+ ...assertionResult,
+ passed: outputMatches
+ ? historicalTestResult.scenarioResults[i].assertionResults[j]
+ .passed
+ : false,
+ failedReason: outputMatches
+ ? historicalTestResult.scenarioResults[i].assertionResults[j]
+ .failedReason
+ : 'AUTOMATED_OUTPUT'
+ })
),
- isSubmit: false,
- context
- });
+ unexpectedBehaviors: null
+ };
+ })
+ });
+
+ return saveTestResultCommon({
+ testResultId: testResult.id,
+ input: convertTestResultToInput(
+ getAutomatedResultFromOutput({
+ baseTestResult: testResult,
+ outputs: responses
+ })
+ ),
+ isSubmit: false,
+ context
+ });
};
const updateJobResults = async (req, res) => {
- const { jobID: id, testRowNumber } = req.params;
- const context = getGraphQLContext({ req });
- const { transaction } = context;
- const {
- responses,
- status,
- capabilities: {
- atName,
- atVersion: atVersionName,
- browserName,
- browserVersion: browserVersionName
- } = {}
- } = req.body;
-
- const job = await getCollectionJobById({ id, transaction });
- if (!job) {
- throwNoJobFoundError(id);
- }
-
- if (job.status !== COLLECTION_JOB_STATUS.RUNNING) {
- throw new Error(
- `Job with id ${id} is not running, cannot update results`
- );
- }
- if (status && !Object.values(COLLECTION_JOB_STATUS).includes(status)) {
- throw new HttpQueryError(400, `Invalid status: ${status}`, true);
- }
- const { testPlanRun } = job;
-
- const testId = (
- await getTestByRowNumber({
- testPlanRun,
- testRowNumber,
- context
- })
- )?.id;
-
- if (testId === undefined) {
- throwNoTestFoundError(testRowNumber);
- }
-
- // status only update, or responses were provided (default to complete)
- if (status || responses) {
- await updateCollectionJobTestStatusByQuery({
- where: { collectionJobId: id, testId },
- // default to completed if not specified (when results are present)
- values: { status: status ?? COLLECTION_JOB_STATUS.COMPLETED },
- transaction: req.transaction
- });
- }
+ const { jobID: id, testRowNumber } = req.params;
+ const context = getGraphQLContext({ req });
+ const { transaction } = context;
+ const {
+ responses,
+ status,
+ capabilities: {
+ atName,
+ atVersion: atVersionName,
+ browserName,
+ browserVersion: browserVersionName
+ } = {}
+ } = req.body;
+
+ const job =
+ req.collectionJob ?? (await getCollectionJobById({ id, transaction }));
+ if (!job) {
+ throwNoJobFoundError(id);
+ }
+
+ if (job.status !== COLLECTION_JOB_STATUS.RUNNING) {
+ throw new Error(`Job with id ${id} is not running, cannot update results`);
+ }
+ if (status && !Object.values(COLLECTION_JOB_STATUS).includes(status)) {
+ throw new HttpQueryError(400, `Invalid status: ${status}`, true);
+ }
+ const { testPlanRun } = job;
+
+ const testId = (
+ await getTestByRowNumber({
+ testPlanRun,
+ testRowNumber,
+ context
+ })
+ )?.id;
+
+ if (testId === undefined) {
+ throwNoTestFoundError(testRowNumber);
+ }
+
+ // status only update, or responses were provided (default to complete)
+ if (status || responses) {
+ await updateCollectionJobTestStatusByQuery({
+ where: { collectionJobId: id, testId },
+ // default to completed if not specified (when results are present)
+ values: { status: status ?? COLLECTION_JOB_STATUS.COMPLETED },
+ transaction: req.transaction
+ });
+ }
+
+ // responses were provided
+ if (responses) {
+ /* TODO: Change this to use a better key based lookup system after gh-958 */
+ const [at] = await getAts({ search: atName, transaction });
+ const [browser] = await getBrowsers({
+ search: browserName,
+ transaction
+ });
- // responses were provided
- if (responses) {
- /* TODO: Change this to use a better key based lookup system after gh-958 */
- const [at] = await getAts({ search: atName, transaction });
- const [browser] = await getBrowsers({
- search: browserName,
- transaction
- });
-
- const [atVersion, browserVersion] = await Promise.all([
- findOrCreateAtVersion({
- where: { atId: at.id, name: atVersionName },
- transaction
- }),
- findOrCreateBrowserVersion({
- where: { browserId: browser.id, name: browserVersionName },
- transaction
- })
- ]);
-
- const processedResponses =
- convertEmptyStringsToNoOutputMessages(responses);
-
- await updateOrCreateTestResultWithResponses({
- testId,
- responses: processedResponses,
- testPlanRun,
- atVersionId: atVersion.id,
- browserVersionId: browserVersion.id,
- context
- });
- }
+ const [atVersion, browserVersion] = await Promise.all([
+ findOrCreateAtVersion({
+ where: { atId: at.id, name: atVersionName },
+ transaction
+ }),
+ findOrCreateBrowserVersion({
+ where: { browserId: browser.id, name: browserVersionName },
+ transaction
+ })
+ ]);
+
+ const processedResponses = convertEmptyStringsToNoOutputMessages(responses);
+
+ await updateOrCreateTestResultWithResponses({
+ testId,
+ responses: processedResponses,
+ testPlanRun,
+ atVersionId: atVersion.id,
+ browserVersionId: browserVersion.id,
+ context
+ });
+ }
- res.json({ success: true });
+ res.json({ success: true });
};
// Human test runners are able to use a checkbox to indicate no output was detected.
// This checkbox stores 'No output was detected.' as the output value for that scenarioResult.
const convertEmptyStringsToNoOutputMessages = outputs =>
- outputs.map(output =>
- output === null || output.trim() === '' ? NO_OUTPUT_STRING : output
- );
+ outputs.map(output =>
+ output === null || output.trim() === '' ? NO_OUTPUT_STRING : output
+ );
module.exports = {
- cancelJob,
- updateJobStatus,
- updateJobResults,
- axiosConfig
+ cancelJob,
+ updateJobStatus,
+ updateJobResults,
+ axiosConfig
};
diff --git a/server/controllers/FakeUserController.js b/server/controllers/FakeUserController.js
index e5a2d5297..ab870ff03 100644
--- a/server/controllers/FakeUserController.js
+++ b/server/controllers/FakeUserController.js
@@ -3,33 +3,31 @@ const { getOrCreateUser } = require('../models/services/UserService');
const ALLOW_FAKE_USER = process.env.ALLOW_FAKE_USER === 'true';
const setFakeUserController = async (req, res) => {
- if (!ALLOW_FAKE_USER) {
- return res
- .status(400)
- .send('Feature not supported in this environment');
- }
+ if (!ALLOW_FAKE_USER) {
+ return res.status(400).send('Feature not supported in this environment');
+ }
- const userToCreate = req.body;
- if (
- !userToCreate ||
- !userToCreate.username ||
- !userToCreate?.roles.length ||
- userToCreate.roles.some(
- role => !['TESTER', 'ADMIN', 'VENDOR'].includes(role.name)
- )
- ) {
- return res.status(400).send('Invalid user');
- }
+ const userToCreate = req.body;
+ if (
+ !userToCreate ||
+ !userToCreate.username ||
+ !userToCreate?.roles.length ||
+ userToCreate.roles.some(
+ role => !['TESTER', 'ADMIN', 'VENDOR'].includes(role.name)
+ )
+ ) {
+ return res.status(400).send('Invalid user');
+ }
- let [user] = await getOrCreateUser({
- where: { username: userToCreate.username },
- values: { roles: userToCreate.roles },
- transaction: req.transaction
- });
+ let [user] = await getOrCreateUser({
+ where: { username: userToCreate.username },
+ values: { roles: userToCreate.roles },
+ transaction: req.transaction
+ });
- req.session.user = user;
+ req.session.user = user;
- res.status(200).send('');
+ res.status(200).send('');
};
module.exports = setFakeUserController;
diff --git a/server/controllers/TestController.js b/server/controllers/TestController.js
index b40cec90c..f59d4bcf1 100644
--- a/server/controllers/TestController.js
+++ b/server/controllers/TestController.js
@@ -1,19 +1,19 @@
const TestService = require('../services/TestService');
async function importTests(req, res) {
- const { git_hash } = req.body;
- try {
- await TestService.importTests(git_hash);
- res.sendStatus(200);
- } catch (error) {
- // eslint-disable-next-line no-console
- console.log(error.message);
- // This is when the script fails because the git hash is invalid
- // Sending semantic error.
- res.sendStatus(422);
- }
+ const { git_hash } = req.body;
+ try {
+ await TestService.importTests(git_hash);
+ res.sendStatus(200);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.log(error.message);
+ // This is when the script fails because the git hash is invalid
+ // Sending semantic error.
+ res.sendStatus(422);
+ }
}
module.exports = {
- importTests
+ importTests
};
diff --git a/server/graphql-context.js b/server/graphql-context.js
index 86c6434d1..2f10719ca 100644
--- a/server/graphql-context.js
+++ b/server/graphql-context.js
@@ -2,16 +2,14 @@ const AtLoader = require('./models/loaders/AtLoader');
const BrowserLoader = require('./models/loaders/BrowserLoader');
const getGraphQLContext = ({ req }) => {
- const user =
- req && req.session && req.session.user ? req.session.user : null;
+ const user = req && req.session && req.session.user ? req.session.user : null;
- // Req will not be defined when queries are made with apolloServer.executeOperation()
- let transaction = req ? req.transaction : false;
+ // Req will not be defined when queries are made with apolloServer.executeOperation()
+ let transaction = req ? req.transaction : false;
- const atLoader = AtLoader();
- const browserLoader = BrowserLoader();
-
- return { user, atLoader, browserLoader, transaction };
+ const atLoader = AtLoader();
+ const browserLoader = BrowserLoader();
+ return { user, atLoader, browserLoader, transaction };
};
module.exports = getGraphQLContext;
diff --git a/server/graphql-schema.js b/server/graphql-schema.js
index 440c04f9f..b6b50adfa 100644
--- a/server/graphql-schema.js
+++ b/server/graphql-schema.js
@@ -1,1573 +1,1577 @@
const { gql } = require('apollo-server');
const graphqlSchema = gql`
+ """
+ Freeform data.
+ """
+ scalar Any
+
+ """
+ The field does not return a response (useful for some mutations).
+ """
+ scalar NoResponse
+
+ """
+ ISO-8601-formatted timestamp.
+ """
+ scalar Timestamp
+
+ """
+ The categories of actions a user can complete in the app.
+ """
+ enum Role {
+ """
+ Whether the user can perform testing. Testers are specified in
+ testers.txt. Note that all admins are testers.
+ """
+ TESTER
+ """
+ Whether the user can perform administrative actions. Admins are members
+ of a special GitHub team, which is different for each app environment.
+ """
+ ADMIN
+ """
+ Whether the user can perform vendor actions, such as reviewing
+ candidate test plans. Vendors are specified in vendors.txt.
+ """
+ VENDOR
+ }
+
+ type User {
+ """
+ Postgres-provided numeric ID.
+ """
+ id: ID!
+ """
+ The GitHub username of the person.
+ """
+ username: String!
+ """
+ List of types of actions the user can complete.
+ """
+ roles: [Role]!
+ """
+ Whether the user is an automation bot user.
+ """
+ isBot: Boolean!
+ # TODO: Either use the recorded data somewhere or eliminate the field.
+ """
+ The ATs the user has indicated they are able to test.
+ """
+ ats: [At]!
+ }
+
+ """
+ The fields of the User type which can be updated after the User has been
+ created. It is a short list since most of the User fields originate outside
+ the app, i.e. the User roles are set by a GitHub team and txt file, and the
+ username is set in GitHub, etc.
+ """
+ input UserInput {
+ """
+ See User type for more information.
+ """
+ atIds: [ID]!
+ }
+
+ """
+ The possible statuses for a CollectionJob.
+ """
+ enum CollectionJobStatus {
+ QUEUED
+ RUNNING
+ COMPLETED
+ ERROR
+ CANCELLED
+ }
+ """
+ A job which was scheduled to collect automated test results using the Response Collection System.
+ """
+ type CollectionJob {
+ """
+ Job Scheduler server-provided ID.
+ """
+ id: ID!
+ """
+ The status of the job, which can be "QUEUED", "RUNNING", "COMPLETED",
+ "ERROR", or "CANCELLED".
+ """
+ status: CollectionJobStatus!
+ """
+ An ID for the Test Plan Run which was created as a result of the Collection Job.
+ This will store the test results.
+ """
+ testPlanRun: TestPlanRun
+ """
+ The URL where the logs for the job can be found.
+ """
+ externalLogsUrl: String
+ """
+ An array of individual test status for every runnable test in the Job.
+ """
+ testStatus: [CollectionJobTestStatus]
+ }
+
+ """
+ A status for a specific Test on a specific CollectionJob.
+ """
+ type CollectionJobTestStatus {
+ """
+ The test this status reflects.
+ """
+ test: Test!
+ """
+ The status of the test, which can be "QUEUED", "RUNNING", "COMPLETED",
+ "ERROR", or "CANCELLED"
+ """
+ status: CollectionJobStatus!
+ }
+
+ type Browser {
+ """
+ Postgres-provided numeric ID.
+ """
+ id: ID!
+ """
+ Browser name like "Chrome".
+ """
+ name: String!
+ """
+ Consistent key name for browser like "chrome" or "safari_macos"
+ """
+ key: String!
+ """
+ A fully-qualified version like "99.0.4844.84"
+ """
+ browserVersions: [BrowserVersion]!
+ """
+ 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]!
+ }
+
+ """
+ A version which has been used to collect test results.
+ """
+ type BrowserVersion {
+ """
+ Postgres-provided numeric ID
+ """
+ id: ID!
+ """
+ Version string
+ """
+ name: String!
+ }
+
+ """
+ The fields on the BrowserVersion type which must be provided to create new
+ BrowserVersions.
+ """
+ input BrowserVersionInput {
+ """
+ See BrowserVersion type for more information.
+ """
+ name: String!
+ }
+
+ """
+ An assistive technology to be tested, such as NVDA or JAWS.
+ """
+ type At {
+ """
+ Postgres-provided numeric ID.
+ """
+ id: ID!
+ """
+ Human-readable name for the AT, such as "NVDA".
+ """
+ name: String!
+ """
+ Consistent key value for lookups.
+ """
+ key: String!
+ """
+ Recorded version numbers
+ """
+ atVersions: [AtVersion]!
+ """
+ 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]!
+ }
+
+ """
+ The version for a given assistive technology.
+ """
+ type AtVersion {
+ """
+ Postgres-provided numeric ID.
+ """
+ id: ID!
+ """
+ Human-readable name for the version, such as "2020.1".
+ """
+ name: String!
+ """
+ Date for approximate availability of the version.
+ """
+ releasedAt: Timestamp!
+ """
+ Whether this AT version is supported by automation.
+ """
+ supportedByAutomation: Boolean!
+ }
+
+ """
+ The fields on the AtVersion type which can be used to create or update the
+ AtVersion.
+ """
+ input AtVersionInput {
+ """
+ See AtVersion type for more information.
+ """
+ name: String!
+ """
+ See AtVersion type for more information.
+ """
+ releasedAt: Timestamp
+ }
+
+ """
+ A suite of tests which keeps its identity as it evolves over time.
+ """
+ type TestPlan {
+ """
+ This is the same as the directory field. Sometimes you want to think of
+ that string as an ID and sometimes you want to explicitly refer to it
+ as a directory, and this allows you to do both.
+ """
+ id: ID!
+ """
+ The formal name of the test plan
+ """
+ title: String!
+ """
+ Corresponds to directory in the ARIA-AT repo which stores the test plan,
+ e.g. "checkbox-tri-state" or "disclosure-navigation"
+ """
+ directory: String!
+ # TODO: determine what to do when a directory is removed from the
+ # ARIA-AT repo
+ # isObsolete: Boolean!
+ """
+ Gets the most recent version imported from the test plan's directory.
+ """
+ latestTestPlanVersion: TestPlanVersion
+ """
+ Gets all historic versions of the test plan.
+ """
+ testPlanVersions: [TestPlanVersion]!
+ """
+ A list of all issues which have filed through "Raise an Issue" buttons
+ in the app. Note that results will be cached for at least ten seconds.
+ """
+ issues: [Issue]!
+ }
+
+ """
+ The life-cycle of a TestPlanVersion from the point it is imported automatically
+ or by an admin until it is saved an available to the public on the reports page.
+ """
+ enum TestPlanVersionPhase {
+ """
+ Accepting new TestPlanRuns from testers.
+ """
+ RD
+ """
+ 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
+ """
+ The TestPlanVersion is now outdated and replaced by another version.
+ """
+ DEPRECATED
+ }
+
+ """
+ A snapshot of time for a test plan, containing all the test plan data,
+ including the actual executable tests.
+ """
+ type TestPlanVersion {
+ """
+ Postgres-provided numeric ID.
+ """
+ id: ID!
+ # TODO: fix bug where the title is missing and make this field required
+ """
+ The title of the TestPlan at this point in time.
+ """
+ title: String
+ """
+ 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.
+ """
+ candidatePhaseReachedAt: Timestamp
+ """
+ Date of when the TestPlanVersion was last updated to the 'Recommended'
+ phase.
+ """
+ recommendedPhaseReachedAt: Timestamp
+ """
+ The intended target date for the final TestPlanVersion phase promotion.
+ Based on the ARIA-AT Working Mode.
+ https://github.com/w3c/aria-at/wiki/Working-Mode
+ """
+ recommendedPhaseTargetDate: Timestamp
+ """
+ The date when the TestPlanVersion was deprecated.
+ """
+ deprecatedAt: Timestamp
+ """
+ The TestPlan this TestPlanVersion is a snapshot of.
+ """
+ testPlan: TestPlan!
+ """
+ A git sha corresponding to the current git commit at the time the
+ version was imported from the ARIA-AT repo. Used to version the test
+ plan over time.
+ """
+ gitSha: String!
+ """
+ Git commit message corresponding to the git sha's commit.
+ """
+ gitMessage: String!
+ """
+ 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!
+ """
+ An easily readable representation of the date associated with the
+ version, formatted like V23.09.28 (or V23.09.28-1 in the case that
+ there are multiple versions on the same day).
+ """
+ versionString: String!
+ # TODO: consider moving to the Scenario type if we support multiple
+ # test pages for one TestPlanVersion (i.e. testing that
+ # and have equivalent behavior).
+ """
+ Link to the HTML page which will be tested.
+ """
+ testPageUrl: String!
+ """
+ Loosely structured data which may or may not be consistent or fully
+ populated across all test plan versions.
+ """
+ metadata: Any
+ """
+ The tests as they stand at this point in time.
+ """
+ tests: [Test]!
+ """
+ The TestPlanReports attached to the 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(isFinal: Boolean): [TestPlanReport]!
+ """
+ A list of existing or missing TestPlanReports that may be collected.
+ """
+ testPlanReportStatuses: [TestPlanReportStatus]!
+ """
+ For each report under this TestPlanVersion, if the report's combination
+ is indicated as required and the report is marked as final at the time
+ the TestPlanVersion is updated to RECOMMENDED then by checking the
+ testers' runs which have been marked as primary, the earliest found AT
+ version for the respective ATs should be considered as the
+ first required AT version or "earliestAtVersion".
+
+ The "earliest" is determined by comparing the recorded AtVersions'
+ releasedAt value.
+
+ Required Reports definition and combinations are defined at
+ https://github.com/w3c/aria-at-app/wiki/Business-Logic-and-Processes#required-reports.
+
+ Primary Test Plan Run is defined at
+ https://github.com/w3c/aria-at-app/wiki/Business-Logic-and-Processes#primary-test-plan-run.
+
+ After this TestPlanVersion is updated to RECOMMENDED, this should be
+ used to ensure subsequent reports created under the TestPlanVersion
+ should only being capturing results for AT Versions which are the same
+ as or were released after the "earliestAtVersion".
+ """
+ earliestAtVersion(atId: ID!): AtVersion
+ }
+
+ """
+ An existing or missing TestPlanReport that can be collected for a given
+ TestPlanVersion.
+ """
+ type TestPlanReportStatus {
+ """
+ Whether the TestPlanReport is actually required during the given
+ TestPlanVersion phase.
+ """
+ isRequired: Boolean!
+ """
+ The report's AT, which will be populated even if the TestPlanReport is
+ missing.
+ """
+ at: At!
+ """
+ The version of the AT that should be used for the report will be
+ specified either as an exactAtVersion or minimumAtVersion and will be
+ populated even if the TestPlanReport is missing.
+
+ During the TestPlanVersion's draft and candidate phases, the looser
+ requirement of minimumAtVersion will be used to reduce the amount of
+ data collected before consensus is achieved.
+
+ During the recommended phase all reports will be associated with an
+ exactAtVersion, enabling large-scale data collection for all versions
+ of the AT as they are released.
+ """
+ exactAtVersion: AtVersion
+ """
+ See exactAtVersion for more information.
+ """
+ minimumAtVersion: AtVersion
+ """
+ The report's browser, which will be populated even if the
+ TestPlanReport is missing.
+ """
+ browser: Browser!
+ """
+ The TestPlanReport, which may not currently exist.
+ """
+ testPlanReport: TestPlanReport
+ }
+
+ """
+ A parsed version of an ARIA-AT test, which, although it may produce multiple
+ executable artifacts (i.e. the scenarios), originated from a single row in
+ the test authoring format CSV maintained in the ARIA-AT repo.
+ """
+ type Test {
+ """
+ A unique ID which encodes some information used by the LocationOfData
+ system.
+ """
+ id: ID!
+ """
+ Since each TestPlan originates from a CSV, this number corresponds to
+ the row within the CSV where this test originated.
+
+ Float type because presentationNumber fields from the source *.csv
+ files use decimal numbers in the v2 format.
+ """
+ rowNumber: Float!
+ """
+ A human-readable sentence describing the function of the test.
+ """
+ title: String!
+ """
+ The ATs the test was written to expect.
+ """
+ ats: [At]!
+
+ """
+ Raw execution-specific data for the Test Renderer such as inputs needed
+ to generate the manual test instructions or links to the setup scripts
+ which must be executed on the testPage. This data is unchanged from its
+ original form found in the ARIA-AT repo's ".collected.json" files. The
+ atId is optional in cases where it can be inferred from context (i.e.
+ the test is a child of a TestPlanReport with a known AT).
+ """
+ renderableContent(atId: ID): Any
+ """
+ For more information, see the renderableContent field. Returns an array
+ containing all renderableContent objects along with the associated AT.
+ """
+ renderableContents: [RenderableContentByAt]!
+ """
+ The URL to a HTML page which loads the Test Renderer and displays
+ the Test. The atId is optional in cases where it can be inferred from
+ context (i.e. the test is a child of a TestPlanReport with a known AT).
+ """
+ renderedUrl(atId: ID): String
+ """
+ For more information, see the renderedUrl field. Returns an array
+ containing all renderedUrls along with the associated AT.
+ """
+ renderedUrls: [RenderedUrlByAt]!
+ """
+ List of ways the test can be completed, each of which needs to be
+ executed separately. There might be a different number of Scenarios
+ for each AT, based on factors like the number of Commands that the AT
+ supports to complete a task.
+ """
+ scenarios(atId: ID): [Scenario]!
+ """
+ Assertions to apply to the output captured for each Scenario. More
+ info on the Assertion type.
+ """
+ assertions(priority: AssertionPriority): [Assertion]!
+ """
+ Vendors who viewed the tests
+ """
+ viewers: [User]
+ """
+ Version number to indicate which of the following test writing specs this test is based on:
+ 1: https://github.com/w3c/aria-at/wiki/Test-Format-V1-Definition
+ 2: https://github.com/w3c/aria-at/wiki/Test-Format-Definition-V2
+ """
+ testFormatVersion: Int!
+ }
+
+ type RenderableContentByAt {
+ at: At!
+ renderableContent: Any!
+ }
+
+ type RenderedUrlByAt {
+ at: At!
+ renderedUrl: String!
+ }
+
+ """
+ A single test may describe a feature which is accessible in many different
+ ways, i.e. there may be several commands which should produce the same
+ output. Instead of writing dozens of nearly-identical tests, the test
+ authoring format allows a single test to have multiple Scenarios, each
+ testing a different command.
+ """
+ type Scenario {
+ """
+ A unique ID which encodes some information used by the LocationOfData
+ system.
+ """
+ id: ID!
+ """
+ The AT which this scenario is testing.
+ """
+ at: At
+ """
+ The commands accomplish the task described in the Test title.
+ Generally there will be a single command, such as "X" or "F", but there
+ can also be a sequence of commands, such as "DOWN", "DOWN", "DOWN".
+ There will be one scenario for each command the AT supports, so
+ navigating to an unchecked checkbox might have four scenarios for the
+ keys "X", "F", "TAB" and "DOWN" which all accomplish that purpose.
+ """
+ commands: [Command]!
+ }
+
+ """
+ A key combination or another kind of AT input, which has a human-readable ID
+ like "TAB" or "DOWN" and a textual representation like "Tab" or "Down
+ Arrow".
+ """
+ type Command {
+ """
+ Human-readable ID which is similar to the text such as "CTRL_OPT_DOWN"
+ """
+ id: ID!
+ """
+ A human-readable version of the command, such as "Control+Alt+Down"
+ """
+ text: String!
+ """
+ The AT mode this command may be getting ran in, such as quickNavOn,
+ browseMode, etc.
+ The same command can be ran during the same test, but in a different
+ mode.
+ """
+ # TODO: Add link to list of known AT modes
+ atOperatingMode: String
+ }
+
+ """
+ Minimal plain representation of a Command.
+ """
+ input CommandInput {
+ """
+ See Command type for more information.
+ """
+ id: ID!
+ }
+
+ """
+ Some assertions are more akin to recommendations or best practices, and,
+ while we want to record whether they are passing or failing, we do not want
+ to count the entire test as failing when they fail.
+ """
+ enum AssertionPriority {
+ """
+ All required assertions must pass for the test to pass. This should be
+ considered as 'MUST Behaviors'.
+ """
+ MUST
+ """
+ This assertion is not considered when deciding if a test is passing.
+ This should be considered as 'SHOULD Behaviors'.
+ """
+ SHOULD
+ # TODO Define MAY
+ MAY
+ """
+ This assertion should not be included in the test and should not be
+ used to determine if the test should pass or fail.
+ This exclusion may be overwritten with an assertion exception.
+ """
+ EXCLUDE
+ }
+
+ """
+ For a given output, the assertion describes a check on that output which can
+ pass or fail.
+ """
+ type Assertion {
+ """
+ A unique ID which encodes some information used by the LocationOfData
+ system.
+ """
+ id: ID!
+ """
+ Whether this assertion contributes to the test failing or not.
+ """
+ priority: AssertionPriority!
+ """
+ A human-readable version of the assertion, like "Role 'radio button' is
+ conveyed".
+ """
+ text: String!
+ """
+ For TestPlanVersions that use the V2 test format, this field contains
+ text like "convey role 'radio button'".
+
+ See the link for more information:
+ https://github.com/w3c/aria-at/wiki/Test-Format-Definition-V2#assertionphrase
+ """
+ phrase: String
+ }
+
+ """
+ TestResults include all the outputs and assertion results which were
+ collected while executing the test, as well as metadata about the execution.
+ """
+ type TestResult {
+ """
+ A unique ID which encodes some information used by the LocationOfData
+ system.
+ """
+ id: ID!
+ """
+ The original test to which the results correspond.
+ """
+ test: Test!
+ """
+ The AtVersion used during this testing session.
+ """
+ atVersion: AtVersion
+ """
+ The BrowserVersion used during this testing session.
+ """
+ browserVersion: BrowserVersion
+ """
+ Automatically set by the server when a new test result is created.
+ """
+ startedAt: Timestamp!
+ # TODO: update explanation once automation is introduced
+ """
+ Automatically set by the server when the test results have been
+ successfully submitted. This means all the scenarios and assertions have
+ been filled in and the user has clicked Submit Results in the UI.
+ """
+ completedAt: Timestamp
+ """
+ The captured output for each of the Scenarios required to test the
+ AT, including the results of all assertions.
+ """
+ scenarioResults: [ScenarioResult]!
+ }
+
+ """
+ Minimal plain representation of a TestResult.
+ """
+ input TestResultInput {
+ """
+ See TestResult type for more information.
+ """
+ id: ID!
+ """
+ See TestResult type for more information.
+ """
+ atVersionId: ID
+ """
+ See TestResult type for more information.
+ """
+ browserVersionId: ID
+ """
+ See TestResult type for more information.
+ """
+ scenarioResults: [ScenarioResultInput]!
+ }
+
+ """
+ For a given scenario, the tester will complete the instructions and then
+ record the output of the AT, which then will be the basis for evaluating
+ whether the assertions passed or failed.
+ """
+ type ScenarioResult {
+ """
+ A unique ID which encodes some information used by the LocationOfData
+ system.
+ """
+ id: ID!
"""
- Freeform data.
- """
- scalar Any
-
- """
- The field does not return a response (useful for some mutations).
- """
- scalar NoResponse
-
- """
- ISO-8601-formatted timestamp.
- """
- scalar Timestamp
-
- """
- The categories of actions a user can complete in the app.
- """
- enum Role {
- """
- Whether the user can perform testing. Testers are specified in
- testers.txt. Note that all admins are testers.
- """
- TESTER
- """
- Whether the user can perform administrative actions. Admins are members
- of a special GitHub team, which is different for each app environment.
- """
- ADMIN
- """
- Whether the user can perform vendor actions, such as reviewing
- candidate test plans. Vendors are specified in vendors.txt.
- """
- VENDOR
- }
-
- type User {
- """
- Postgres-provided numeric ID.
- """
- id: ID!
- """
- The GitHub username of the person.
- """
- username: String!
- """
- List of types of actions the user can complete.
- """
- roles: [Role]!
- """
- Whether the user is an automation bot user.
- """
- isBot: Boolean!
- # TODO: Either use the recorded data somewhere or eliminate the field.
- """
- The ATs the user has indicated they are able to test.
- """
- ats: [At]!
- }
-
- """
- The fields of the User type which can be updated after the User has been
- created. It is a short list since most of the User fields originate outside
- the app, i.e. the User roles are set by a GitHub team and txt file, and the
- username is set in GitHub, etc.
- """
- input UserInput {
- """
- See User type for more information.
- """
- atIds: [ID]!
- }
-
- """
- The possible statuses for a CollectionJob.
- """
- enum CollectionJobStatus {
- QUEUED
- RUNNING
- COMPLETED
- ERROR
- CANCELLED
- }
- """
- A job which was scheduled to collect automated test results using the Response Collection System.
- """
- type CollectionJob {
- """
- Job Scheduler server-provided ID.
- """
- id: ID!
- """
- The status of the job, which can be "QUEUED", "RUNNING", "COMPLETED",
- "ERROR", or "CANCELLED".
- """
- status: CollectionJobStatus!
- """
- An ID for the Test Plan Run which was created as a result of the Collection Job.
- This will store the test results.
- """
- testPlanRun: TestPlanRun
- """
- The URL where the logs for the job can be found.
- """
- externalLogsUrl: String
- """
- An array of individual test status for every runnable test in the Job.
- """
- testStatus: [CollectionJobTestStatus]
- }
-
- """
- A status for a specific Test on a specific CollectionJob.
- """
- type CollectionJobTestStatus {
- """
- The test this status reflects.
- """
- test: Test!
- """
- The status of the test, which can be "QUEUED", "RUNNING", "COMPLETED",
- "ERROR", or "CANCELLED"
- """
- status: CollectionJobStatus!
- }
-
- type Browser {
- """
- Postgres-provided numeric ID.
- """
- id: ID!
- """
- Browser name like "Chrome".
- """
- name: String!
- """
- Consistent key name for browser like "chrome" or "safari_macos"
- """
- key: String!
- """
- A fully-qualified version like "99.0.4844.84"
- """
- browserVersions: [BrowserVersion]!
- """
- 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]!
- }
-
- """
- A version which has been used to collect test results.
- """
- type BrowserVersion {
- """
- Postgres-provided numeric ID
- """
- id: ID!
- """
- Version string
- """
- name: String!
- }
-
- """
- The fields on the BrowserVersion type which must be provided to create new
- BrowserVersions.
- """
- input BrowserVersionInput {
- """
- See BrowserVersion type for more information.
- """
- name: String!
- }
-
- """
- An assistive technology to be tested, such as NVDA or JAWS.
- """
- type At {
- """
- Postgres-provided numeric ID.
- """
- id: ID!
- """
- Human-readable name for the AT, such as "NVDA".
- """
- name: String!
- """
- Consistent key value for lookups.
- """
- key: String!
- """
- Recorded version numbers
- """
- atVersions: [AtVersion]!
- """
- 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]!
- }
-
- """
- The version for a given assistive technology.
- """
- type AtVersion {
- """
- Postgres-provided numeric ID.
- """
- id: ID!
- """
- Human-readable name for the version, such as "2020.1".
- """
- name: String!
- """
- Date for approximate availability of the version.
- """
- releasedAt: Timestamp!
- }
-
- """
- The fields on the AtVersion type which can be used to create or update the
- AtVersion.
- """
- input AtVersionInput {
- """
- See AtVersion type for more information.
- """
- name: String!
- """
- See AtVersion type for more information.
- """
- releasedAt: Timestamp
- }
-
- """
- A suite of tests which keeps its identity as it evolves over time.
- """
- type TestPlan {
- """
- This is the same as the directory field. Sometimes you want to think of
- that string as an ID and sometimes you want to explicitly refer to it
- as a directory, and this allows you to do both.
- """
- id: ID!
- """
- The formal name of the test plan
- """
- title: String!
- """
- Corresponds to directory in the ARIA-AT repo which stores the test plan,
- e.g. "checkbox-tri-state" or "disclosure-navigation"
- """
- directory: String!
- # TODO: determine what to do when a directory is removed from the
- # ARIA-AT repo
- # isObsolete: Boolean!
- """
- Gets the most recent version imported from the test plan's directory.
- """
- latestTestPlanVersion: TestPlanVersion
- """
- Gets all historic versions of the test plan.
- """
- testPlanVersions: [TestPlanVersion]!
- """
- A list of all issues which have filed through "Raise an Issue" buttons
- in the app. Note that results will be cached for at least ten seconds.
- """
- issues: [Issue]!
- }
-
- """
- The life-cycle of a TestPlanVersion from the point it is imported automatically
- or by an admin until it is saved an available to the public on the reports page.
- """
- enum TestPlanVersionPhase {
- """
- Accepting new TestPlanRuns from testers.
- """
- RD
- """
- 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
- """
- The TestPlanVersion is now outdated and replaced by another version.
- """
- DEPRECATED
- }
-
- """
- A snapshot of time for a test plan, containing all the test plan data,
- including the actual executable tests.
- """
- type TestPlanVersion {
- """
- Postgres-provided numeric ID.
- """
- id: ID!
- # TODO: fix bug where the title is missing and make this field required
- """
- The title of the TestPlan at this point in time.
- """
- title: String
- """
- 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.
- """
- candidatePhaseReachedAt: Timestamp
- """
- Date of when the TestPlanVersion was last updated to the 'Recommended'
- phase.
- """
- recommendedPhaseReachedAt: Timestamp
- """
- The intended target date for the final TestPlanVersion phase promotion.
- Based on the ARIA-AT Working Mode.
- https://github.com/w3c/aria-at/wiki/Working-Mode
- """
- recommendedPhaseTargetDate: Timestamp
- """
- The date when the TestPlanVersion was deprecated.
- """
- deprecatedAt: Timestamp
- """
- The TestPlan this TestPlanVersion is a snapshot of.
- """
- testPlan: TestPlan!
- """
- A git sha corresponding to the current git commit at the time the
- version was imported from the ARIA-AT repo. Used to version the test
- plan over time.
- """
- gitSha: String!
- """
- Git commit message corresponding to the git sha's commit.
- """
- gitMessage: String!
- """
- 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!
- """
- An easily readable representation of the date associated with the
- version, formatted like V23.09.28 (or V23.09.28-1 in the case that
- there are multiple versions on the same day).
- """
- versionString: String!
- # TODO: consider moving to the Scenario type if we support multiple
- # test pages for one TestPlanVersion (i.e. testing that
- # and have equivalent behavior).
- """
- Link to the HTML page which will be tested.
- """
- testPageUrl: String!
- """
- Loosely structured data which may or may not be consistent or fully
- populated across all test plan versions.
- """
- metadata: Any
- """
- The tests as they stand at this point in time.
- """
- tests: [Test]!
- """
- The TestPlanReports attached to the 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(isFinal: Boolean): [TestPlanReport]!
- """
- A list of existing or missing TestPlanReports that may be collected.
- """
- testPlanReportStatuses: [TestPlanReportStatus]!
- """
- For each report under this TestPlanVersion, if the report's combination
- is indicated as required and the report is marked as final at the time
- the TestPlanVersion is updated to RECOMMENDED then by checking the
- testers' runs which have been marked as primary, the earliest found AT
- version for the respective ATs should be considered as the
- first required AT version or "earliestAtVersion".
-
- The "earliest" is determined by comparing the recorded AtVersions'
- releasedAt value.
-
- Required Reports definition and combinations are defined at
- https://github.com/w3c/aria-at-app/wiki/Business-Logic-and-Processes#required-reports.
-
- Primary Test Plan Run is defined at
- https://github.com/w3c/aria-at-app/wiki/Business-Logic-and-Processes#primary-test-plan-run.
-
- After this TestPlanVersion is updated to RECOMMENDED, this should be
- used to ensure subsequent reports created under the TestPlanVersion
- should only being capturing results for AT Versions which are the same
- as or were released after the "earliestAtVersion".
- """
- earliestAtVersion(atId: ID!): AtVersion
- }
-
- """
- An existing or missing TestPlanReport that can be collected for a given
- TestPlanVersion.
- """
- type TestPlanReportStatus {
- """
- Whether the TestPlanReport is actually required during the given
- TestPlanVersion phase.
- """
- isRequired: Boolean!
- """
- The report's AT, which will be populated even if the TestPlanReport is
- missing.
- """
- at: At!
- """
- The version of the AT that should be used for the report will be
- specified either as an exactAtVersion or minimumAtVersion and will be
- populated even if the TestPlanReport is missing.
-
- During the TestPlanVersion's draft and candidate phases, the looser
- requirement of minimumAtVersion will be used to reduce the amount of
- data collected before consensus is achieved.
-
- During the recommended phase all reports will be associated with an
- exactAtVersion, enabling large-scale data collection for all versions
- of the AT as they are released.
- """
- exactAtVersion: AtVersion
- """
- See exactAtVersion for more information.
- """
- minimumAtVersion: AtVersion
- """
- The report's browser, which will be populated even if the
- TestPlanReport is missing.
- """
- browser: Browser!
- """
- The TestPlanReport, which may not currently exist.
- """
- testPlanReport: TestPlanReport
- }
-
- """
- A parsed version of an ARIA-AT test, which, although it may produce multiple
- executable artifacts (i.e. the scenarios), originated from a single row in
- the test authoring format CSV maintained in the ARIA-AT repo.
- """
- type Test {
- """
- A unique ID which encodes some information used by the LocationOfData
- system.
- """
- id: ID!
- """
- Since each TestPlan originates from a CSV, this number corresponds to
- the row within the CSV where this test originated.
-
- Float type because presentationNumber fields from the source *.csv
- files use decimal numbers in the v2 format.
- """
- rowNumber: Float!
- """
- A human-readable sentence describing the function of the test.
- """
- title: String!
- """
- The ATs the test was written to expect.
- """
- ats: [At]!
-
- """
- Raw execution-specific data for the Test Renderer such as inputs needed
- to generate the manual test instructions or links to the setup scripts
- which must be executed on the testPage. This data is unchanged from its
- original form found in the ARIA-AT repo's ".collected.json" files. The
- atId is optional in cases where it can be inferred from context (i.e.
- the test is a child of a TestPlanReport with a known AT).
- """
- renderableContent(atId: ID): Any
- """
- For more information, see the renderableContent field. Returns an array
- containing all renderableContent objects along with the associated AT.
- """
- renderableContents: [RenderableContentByAt]!
- """
- The URL to a HTML page which loads the Test Renderer and displays
- the Test. The atId is optional in cases where it can be inferred from
- context (i.e. the test is a child of a TestPlanReport with a known AT).
- """
- renderedUrl(atId: ID): String
- """
- For more information, see the renderedUrl field. Returns an array
- containing all renderedUrls along with the associated AT.
- """
- renderedUrls: [RenderedUrlByAt]!
- """
- List of ways the test can be completed, each of which needs to be
- executed separately. There might be a different number of Scenarios
- for each AT, based on factors like the number of Commands that the AT
- supports to complete a task.
- """
- scenarios(atId: ID): [Scenario]!
- """
- Assertions to apply to the output captured for each Scenario. More
- info on the Assertion type.
- """
- assertions(priority: AssertionPriority): [Assertion]!
- """
- Vendors who viewed the tests
- """
- viewers: [User]
- """
- Version number to indicate which of the following test writing specs this test is based on:
- 1: https://github.com/w3c/aria-at/wiki/Test-Format-V1-Definition
- 2: https://github.com/w3c/aria-at/wiki/Test-Format-Definition-V2
- """
- testFormatVersion: Int!
- }
-
- type RenderableContentByAt {
- at: At!
- renderableContent: Any!
- }
-
- type RenderedUrlByAt {
- at: At!
- renderedUrl: String!
- }
-
- """
- A single test may describe a feature which is accessible in many different
- ways, i.e. there may be several commands which should produce the same
- output. Instead of writing dozens of nearly-identical tests, the test
- authoring format allows a single test to have multiple Scenarios, each
- testing a different command.
- """
- type Scenario {
- """
- A unique ID which encodes some information used by the LocationOfData
- system.
- """
- id: ID!
- """
- The AT which this scenario is testing.
- """
- at: At
- """
- The commands accomplish the task described in the Test title.
- Generally there will be a single command, such as "X" or "F", but there
- can also be a sequence of commands, such as "DOWN", "DOWN", "DOWN".
- There will be one scenario for each command the AT supports, so
- navigating to an unchecked checkbox might have four scenarios for the
- keys "X", "F", "TAB" and "DOWN" which all accomplish that purpose.
- """
- commands: [Command]!
- }
-
- """
- A key combination or another kind of AT input, which has a human-readable ID
- like "TAB" or "DOWN" and a textual representation like "Tab" or "Down
- Arrow".
- """
- type Command {
- """
- Human-readable ID which is similar to the text such as "CTRL_OPT_DOWN"
- """
- id: ID!
- """
- A human-readable version of the command, such as "Control+Alt+Down"
- """
- text: String!
- """
- The AT mode this command may be getting ran in, such as quickNavOn,
- browseMode, etc.
- The same command can be ran during the same test, but in a different
- mode.
- """
- # TODO: Add link to list of known AT modes
- atOperatingMode: String
- }
-
- """
- Minimal plain representation of a Command.
- """
- input CommandInput {
- """
- See Command type for more information.
- """
- id: ID!
- }
-
- """
- Some assertions are more akin to recommendations or best practices, and,
- while we want to record whether they are passing or failing, we do not want
- to count the entire test as failing when they fail.
- """
- enum AssertionPriority {
- """
- All required assertions must pass for the test to pass. This should be
- considered as 'MUST Behaviors'.
- """
- MUST
- """
- This assertion is not considered when deciding if a test is passing.
- This should be considered as 'SHOULD Behaviors'.
- """
- SHOULD
- # TODO Define MAY
- MAY
- """
- This assertion should not be included in the test and should not be
- used to determine if the test should pass or fail.
- This exclusion may be overwritten with an assertion exception.
- """
- EXCLUDE
- }
-
- """
- For a given output, the assertion describes a check on that output which can
- pass or fail.
- """
- type Assertion {
- """
- A unique ID which encodes some information used by the LocationOfData
- system.
- """
- id: ID!
- """
- Whether this assertion contributes to the test failing or not.
- """
- priority: AssertionPriority!
- """
- A human-readable version of the assertion, like "Role 'radio button' is
- conveyed".
- """
- text: String!
- """
- For TestPlanVersions that use the V2 test format, this field contains
- text like "convey role 'radio button'".
-
- See the link for more information:
- https://github.com/w3c/aria-at/wiki/Test-Format-Definition-V2#assertionphrase
- """
- phrase: String
- }
-
- """
- TestResults include all the outputs and assertion results which were
- collected while executing the test, as well as metadata about the execution.
- """
- type TestResult {
- """
- A unique ID which encodes some information used by the LocationOfData
- system.
- """
- id: ID!
- """
- The original test to which the results correspond.
- """
- test: Test!
- """
- The AtVersion used during this testing session.
- """
- atVersion: AtVersion
- """
- The BrowserVersion used during this testing session.
- """
- browserVersion: BrowserVersion
- """
- Automatically set by the server when a new test result is created.
- """
- startedAt: Timestamp!
- # TODO: update explanation once automation is introduced
- """
- Automatically set by the server when the test results have been
- successfully submitted. This means all the scenarios and assertions have
- been filled in and the user has clicked Submit Results in the UI.
- """
- completedAt: Timestamp
- """
- The captured output for each of the Scenarios required to test the
- AT, including the results of all assertions.
- """
- scenarioResults: [ScenarioResult]!
- }
-
- """
- Minimal plain representation of a TestResult.
- """
- input TestResultInput {
- """
- See TestResult type for more information.
- """
- id: ID!
- """
- See TestResult type for more information.
- """
- atVersionId: ID
- """
- See TestResult type for more information.
- """
- browserVersionId: ID
- """
- See TestResult type for more information.
- """
- scenarioResults: [ScenarioResultInput]!
- }
-
- """
- For a given scenario, the tester will complete the instructions and then
- record the output of the AT, which then will be the basis for evaluating
- whether the assertions passed or failed.
- """
- type ScenarioResult {
- """
- A unique ID which encodes some information used by the LocationOfData
- system.
- """
- id: ID!
- """
- The original Scenario to which the result corresponds.
- """
- scenario: Scenario!
- """
- After each scenario is executed, the tester will capture the output of
- the AT, and that output will be used as the basis for the assertions.
- Submitted test results require this field to be filled in.
- """
- output: String
- """
- The outcomes of the assertions based on the output field.
- """
- assertionResults(priority: AssertionPriority): [AssertionResult]!
- """
- Failure states like "AT became excessively sluggish" which would count
- as a failure for any scenario, even when the assertions otherwise pass.
- Submitted test results require this field to be filled in.
- """
- unexpectedBehaviors: [UnexpectedBehavior]
- }
-
- """
- Minimal plain representation of a ScenarioResult.
- """
- input ScenarioResultInput {
- """
- See ScenarioResult type for more information.
- """
- id: ID!
- """
- See ScenarioResult type for more information.
- """
- output: String
- """
- See ScenarioResult type for more information.
- """
- assertionResults: [AssertionResultInput]!
- """
- See ScenarioResult type for more information.
- """
- unexpectedBehaviors: [UnexpectedBehaviorInput]
- }
-
- # NOTE: This has been deprecated
- enum AssertionFailedReason {
- INCORRECT_OUTPUT
- NO_OUTPUT
- AUTOMATED_OUTPUT
- }
-
- """
- Whether an assertion passed or failed.
- """
- type AssertionResult {
- """
- A unique ID which encodes some information used by the LocationOfData
- system.
- """
- id: ID!
- """
- The original Assertion to which the result corresponds.
- """
- assertion: Assertion!
- """
- Whether the assertion is considered passing or failing. Submitted test
- results require this field to be filled in.
- """
- passed: Boolean
- # TODO: propose removing this for the reason given above
- """
- NOTE: This has been deprecated, legacy use = when passed is false, a failedReason must be given.
- """
- failedReason: AssertionFailedReason
- }
-
- """
- Minimal plain representation of an AssertionResult.
- """
- input AssertionResultInput {
- """
- See Assertion for more information.
- """
- id: ID!
- """
- See Assertion for more information.
- """
- passed: Boolean
- """
- See Assertion for more information.
- """
- failedReason: AssertionFailedReason
- }
-
- enum UnexpectedBehaviorId {
- EXCESSIVELY_VERBOSE
- UNEXPECTED_CURSOR_POSITION
- SLUGGISH
- AT_CRASHED
- BROWSER_CRASHED
- OTHER
- }
-
- enum UnexpectedBehaviorImpact {
- MODERATE
- SEVERE
- }
-
- """
- A failure state such as "AT became excessively sluggish" which, if it
- occurs, should count as a scenario failure.
- """
- type UnexpectedBehavior {
- """
- Human-readable ID, e.g. "excessively_sluggish" which is similar to the
- text.
- """
- id: UnexpectedBehaviorId!
- """
- Human-readable sentence describing the failure.
- """
- text: String!
- """
- The user must provide text explaining what occurred.
- """
- details: String!
- """
- The user must indicate the severity of the behavior.
- """
- impact: UnexpectedBehaviorImpact!
- }
-
- """
- Minimal plain representation of an UnexpectedBehavior.
- """
- input UnexpectedBehaviorInput {
- """
- See UnexpectedBehavior for more information.
- """
- id: UnexpectedBehaviorId!
- """
- See UnexpectedBehavior for more information.
- """
- details: String!
- """
- See UnexpectedBehavior for more information.
- """
- impact: UnexpectedBehaviorImpact!
- }
-
- """
- Records information about the execution of a TestPlan.
- """
- type TestPlanRun {
- """
- Postgres-provided numeric ID.
- """
- id: ID!
- """
- The person who executed the tests.
- """
- tester: User!
- """
- The TestPlanReport this TestPlanRun is a part of.
- """
- testPlanReport: TestPlanReport!
- """
- Array of results, each of which correspond to one Test which can be
- found on the TestPlanVersion type.
- """
- testResults: [TestResult]!
- """
- The number of completed tests for this TestPlanRun. Foregoes the need of
- getting the length from testResults which would require running
- expensive time-consuming operations to calculate.
- """
- testResultsLength: Int!
- """
- Whether the TestPlanRun was initiated by the Response Collection System
- """
- initiatedByAutomation: Boolean!
- """
- The CollectionJob related to this testPlanRun
- """
- collectionJob: CollectionJob
- }
-
- """
- 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
- the cause of the disparity is determined.
- """
- type TestPlanReportConflict {
- """
- The part of the test where the disagreement occurred. This does not
- include the actual results and merely points to the differing scenario
- or assertion.
- """
- source: PopulatedData!
- """
- The two-or-more sets of test results which do not match. If the conflict
- occurred in an assertion, for example, the populated data would include
- a testPlanRun, scenarioResult and assertionResult for each of the
- results which differed (as well as the other associated data
- PopulatedData will make available).
- """
- conflictingResults: [PopulatedData]!
- }
+ The original Scenario to which the result corresponds.
+ """
+ scenario: Scenario!
+ """
+ After each scenario is executed, the tester will capture the output of
+ the AT, and that output will be used as the basis for the assertions.
+ Submitted test results require this field to be filled in.
+ """
+ output: String
+ """
+ The outcomes of the assertions based on the output field.
+ """
+ assertionResults(priority: AssertionPriority): [AssertionResult]!
+ """
+ Failure states like "AT became excessively sluggish" which would count
+ as a failure for any scenario, even when the assertions otherwise pass.
+ Submitted test results require this field to be filled in.
+ """
+ unexpectedBehaviors: [UnexpectedBehavior]
+ }
+
+ """
+ Minimal plain representation of a ScenarioResult.
+ """
+ input ScenarioResultInput {
+ """
+ See ScenarioResult type for more information.
+ """
+ id: ID!
+ """
+ See ScenarioResult type for more information.
+ """
+ output: String
+ """
+ See ScenarioResult type for more information.
+ """
+ assertionResults: [AssertionResultInput]!
+ """
+ See ScenarioResult type for more information.
+ """
+ unexpectedBehaviors: [UnexpectedBehaviorInput]
+ }
+
+ # NOTE: This has been deprecated
+ enum AssertionFailedReason {
+ INCORRECT_OUTPUT
+ NO_OUTPUT
+ AUTOMATED_OUTPUT
+ }
+
+ """
+ Whether an assertion passed or failed.
+ """
+ type AssertionResult {
+ """
+ A unique ID which encodes some information used by the LocationOfData
+ system.
+ """
+ id: ID!
+ """
+ The original Assertion to which the result corresponds.
+ """
+ assertion: Assertion!
+ """
+ Whether the assertion is considered passing or failing. Submitted test
+ results require this field to be filled in.
+ """
+ passed: Boolean
+ # TODO: propose removing this for the reason given above
+ """
+ NOTE: This has been deprecated, legacy use = when passed is false, a failedReason must be given.
+ """
+ failedReason: AssertionFailedReason
+ }
+
+ """
+ Minimal plain representation of an AssertionResult.
+ """
+ input AssertionResultInput {
+ """
+ See Assertion for more information.
+ """
+ id: ID!
+ """
+ See Assertion for more information.
+ """
+ passed: Boolean
+ """
+ See Assertion for more information.
+ """
+ failedReason: AssertionFailedReason
+ }
+
+ enum UnexpectedBehaviorId {
+ EXCESSIVELY_VERBOSE
+ UNEXPECTED_CURSOR_POSITION
+ SLUGGISH
+ AT_CRASHED
+ BROWSER_CRASHED
+ OTHER
+ }
+
+ enum UnexpectedBehaviorImpact {
+ MODERATE
+ SEVERE
+ }
+
+ """
+ A failure state such as "AT became excessively sluggish" which, if it
+ occurs, should count as a scenario failure.
+ """
+ type UnexpectedBehavior {
+ """
+ Human-readable ID, e.g. "excessively_sluggish" which is similar to the
+ text.
+ """
+ id: UnexpectedBehaviorId!
+ """
+ Human-readable sentence describing the failure.
+ """
+ text: String!
+ """
+ The user must provide text explaining what occurred.
+ """
+ details: String!
+ """
+ The user must indicate the severity of the behavior.
+ """
+ impact: UnexpectedBehaviorImpact!
+ }
+
+ """
+ Minimal plain representation of an UnexpectedBehavior.
+ """
+ input UnexpectedBehaviorInput {
+ """
+ See UnexpectedBehavior for more information.
+ """
+ id: UnexpectedBehaviorId!
+ """
+ See UnexpectedBehavior for more information.
+ """
+ details: String!
+ """
+ See UnexpectedBehavior for more information.
+ """
+ impact: UnexpectedBehaviorImpact!
+ }
+
+ """
+ Records information about the execution of a TestPlan.
+ """
+ type TestPlanRun {
+ """
+ Postgres-provided numeric ID.
+ """
+ id: ID!
+ """
+ The person who executed the tests.
+ """
+ tester: User!
+ """
+ The TestPlanReport this TestPlanRun is a part of.
+ """
+ testPlanReport: TestPlanReport!
+ """
+ Array of results, each of which correspond to one Test which can be
+ found on the TestPlanVersion type.
+ """
+ testResults: [TestResult]!
+ """
+ The number of completed tests for this TestPlanRun. Foregoes the need of
+ getting the length from testResults which would require running
+ expensive time-consuming operations to calculate.
+ """
+ testResultsLength: Int!
+ """
+ Whether the TestPlanRun was initiated by the Response Collection System
+ """
+ initiatedByAutomation: Boolean!
+ """
+ The CollectionJob related to this testPlanRun
+ """
+ collectionJob: CollectionJob
+ }
+
+ """
+ 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
+ the cause of the disparity is determined.
+ """
+ type TestPlanReportConflict {
+ """
+ The part of the test where the disagreement occurred. This does not
+ include the actual results and merely points to the differing scenario
+ or assertion.
+ """
+ source: PopulatedData!
+ """
+ The two-or-more sets of test results which do not match. If the conflict
+ occurred in an assertion, for example, the populated data would include
+ a testPlanRun, scenarioResult and assertionResult for each of the
+ results which differed (as well as the other associated data
+ PopulatedData will make available).
+ """
+ conflictingResults: [PopulatedData]!
+ }
+
+ """
+ Indicates the type of issue. 'CHANGES_REQUESTED' or 'FEEDBACK'.
+ 'FEEDBACK' is the default type.
+ """
+ enum IssueFeedbackType {
+ FEEDBACK
+ CHANGES_REQUESTED
+ }
+ type Issue {
+ """
+ GitHub username of the issue creator.
+ """
+ author: String!
+ """
+ The issue title in GitHub.
+ """
+ title: String!
+ """
+ Link to the GitHub issue's first comment.
+ """
+ link: String!
+ """
+ Will be true if the issue was raised on the Candidate Review page
+ of the app (as opposed to other places with "raise an issue" buttons like
+ the test queue or the reports page.)
+ """
+ isCandidateReview: Boolean!
"""
Indicates the type of issue. 'CHANGES_REQUESTED' or 'FEEDBACK'.
'FEEDBACK' is the default type.
"""
- enum IssueFeedbackType {
- FEEDBACK
- CHANGES_REQUESTED
- }
-
- type Issue {
- """
- GitHub username of the issue creator.
- """
- author: String!
- """
- The issue title in GitHub.
- """
- title: String!
- """
- Link to the GitHub issue's first comment.
- """
- link: String!
- """
- Will be true if the issue was raised on the Candidate Review page
- of the app (as opposed to other places with "raise an issue" buttons like
- the test queue or the reports page.)
- """
- isCandidateReview: Boolean!
- """
- Indicates the type of issue. 'CHANGES_REQUESTED' or 'FEEDBACK'.
- 'FEEDBACK' is the default type.
- """
- feedbackType: IssueFeedbackType!
- """
- Indicates if the issue is currently open on GitHub.
- """
- isOpen: Boolean!
- """
- Test Number the issue was raised for.
- """
- testNumberFilteredByAt: Int
- """
- The time the issue was created, according to GitHub.
- """
- createdAt: Timestamp!
- """
- The time the issue was closed, if it was closed.
- """
- closedAt: Timestamp
- """
- The AT associated with the issue. Although there are not currently any
- cases where we generate GitHub issues without an associated AT, that
- may not remain true forever and we do support this field being
- undefined.
- """
- at: At
- """
- The browser associated with the issue, which may not be present.
- """
- browser: Browser
- }
-
- """
- A container for test results as captured by multiple testers. The tests to
- be run for a TestPlanReport originate in the TestPlanVersion.
- """
- type TestPlanReport {
- """
- Postgres-provided numeric ID.
- """
- id: ID!
- """
- The snapshot of a TestPlan to use.
- """
- testPlanVersion: TestPlanVersion!
- """
- The AT used when collecting results.
- """
- at: At!
- """
- Either a minimumAtVersion or exactAtVersion will be available. The
- minimumAtVersion, when defined, is the oldest version of the AT that
- testers are allowed to use when collecting results.
- """
- minimumAtVersion: AtVersion
- """
- Either a minimumAtVersion or exactAtVersion will be available. The
- exactAtVersion, when defined, is the only version of the AT that
- testers are allowed to use when collecting results. Note that when a
- TestPlanVersion reaches the recommended stage, all its reports will
- automatically switch from having a minimumAtVersion to an
- exactAtVersion. See the earliestAtVersion field of TestPlanVersion for
- more information.
- """
- exactAtVersion: AtVersion
- """
- The unique AT Versions used when collecting results for this report.
- """
- atVersions: [AtVersion]!
- """
- The latest AT Version used collecting results for this report.
- """
- latestAtVersionReleasedAt: AtVersion
- """
- The browser used when collecting results.
- """
- browser: Browser!
- """
- The subset of tests which are relevant to this report, i.e. the tests
- where the AT matches the report's AT.
- """
- runnableTests: [Test]!
- """
- The number of tests available for this TestPlanReport's AT. Foregoes the
- need of getting the length from runnableTests which would require
- running expensive time-consuming operations to calculate.
- """
- runnableTestsLength: Int!
- """
- A list of conflicts between runs, which may occur at the level of the
- 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 TestPlanVersion phase can change from
- DRAFT to CANDIDATE.
- """
- conflicts: [TestPlanReportConflict]!
- """
- The number of conflicts for this TestPlanReport. Foregoes the need for
- getting the length from conflicts which would require running expensive
- time-consuming operations to calculate.
- """
- conflictsLength: Int!
- """
- Finalizing a test plan report requires resolving any conflicts between
- runs. At this stage a single set of results is able to represent all
- results, and is much more convenient to work with.
- """
- finalizedTestResults: [TestResult]
- """
- A list of all issues which have filed through "Raise an Issue" buttons
- in the app. Note that results will be cached for at least ten seconds.
- """
- issues: [Issue]!
- """
- These are all the TestPlanRuns which were recorded during the
- TestPlanReport's DRAFT stage.
- """
- draftTestPlanRuns: [TestPlanRun]!
- """
- The state of the vendor review, which can be "READY", "IN_PROGRESS", and "APPROVED"
- """
- vendorReviewStatus: String
- """
- Various metrics and calculations related to the TestPlanReport which
- may be used for reporting purposes.
- """
- metrics: Any!
- """
- 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!
- """
- The AtVersion to display for a TestPlanReport only when the
- TestPlanVersion is RECOMMENDED.
-
- If this TestPlanReport was created with an "exactAtVersionId" being set,
- it will use the matching AtVersion, otherwise it will use the
- TestPlanVersion.earliestAtVersion as a default.
- """
- recommendedAtVersion: AtVersion
- }
-
- """
- Minimal plain representation of a TestPlanReport.
- """
- input TestPlanReportInput {
- testPlanVersionId: ID!
- atId: ID!
- exactAtVersionId: ID
- minimumAtVersionId: ID
- browserId: ID!
- copyResultsFromTestPlanVersionId: ID
- }
-
- """
- Allows you to provide an ID, any ID, and load all the data which
- can be found through relationships to that ID. For example, if you have a
- scenarioResultId, it is possible to find a ScenarioResult, Scenario,
- TestResult, Test, TestPlanVersion, TestPlanTarget, At, AtVersion, Browser,
- BrowserVersion, and TestPlan. This can save a lot of effort wrangling data!
- """
- input LocationOfDataInput {
- testPlanId: ID
- testPlanVersionId: ID
- testId: ID
- scenarioId: ID
- assertionId: ID
- testPlanReportId: ID
- browserId: ID
- browserVersionId: ID
- atId: ID
- atVersionId: ID
- testPlanRunId: ID
- testResultId: ID
- scenarioResultId: ID
- assertionResultId: ID
- }
- """
- Contains the same fields as the LocationOfDataInput type. This is used to
- return a LocationOfData in queries in the same form you can provide it as an
- input type. It is a scalar because otherwise you would need to request every
- single field, which would be hugely inconvenient.
- """
- scalar LocationOfData
-
- """
- The fully-populated data which is associated with a given LocationOfData.
- For example, a LocationOfData which includes an ID for a TestPlanReport
- would allow you to populate the TestPlanVersion, AT, Browser and TestPlan,
- which are knowable through relationships to that TestPlanReport.
- """
- type PopulatedData {
- locationOfData: LocationOfData!
- testPlan: TestPlan
- testPlanVersion: TestPlanVersion
- at: At
- atVersion: AtVersion
- browser: Browser
- browserVersion: BrowserVersion
- test: Test
- scenario: Scenario
- assertion: Assertion
- testPlanReport: TestPlanReport
- testPlanRun: TestPlanRun
- testResult: TestResult
- scenarioResult: ScenarioResult
- assertionResult: AssertionResult
- }
-
- type Query {
- """
- Get the currently-logged-in user or null if you are not logged in.
- """
- me: User
- """
- Get all registered users. Must be logged in.
- """
- users: [User]!
- """
- Get all assistive technologies known to the app.
- """
- ats: [At]!
- """
- Get all browsers known to the app.
- """
- browsers: [Browser]!
- """
- Get all TestPlans.
- """
- testPlans(testPlanVersionPhases: [TestPlanVersionPhase]): [TestPlan]!
- """
- Load a particular TestPlan by ID.
- """
- testPlan(id: ID!): TestPlan
- """
- Get all TestPlanVersions.
- """
- testPlanVersions(
- phases: [TestPlanVersionPhase]
- directory: String
- ): [TestPlanVersion]!
- """
- Get a particular TestPlanVersion by ID.
- """
- testPlanVersion(id: ID): TestPlanVersion
- """
- Load multiple TestPlanReports, with the optional ability to filter by
- TestPlanVersionPhase, atId, testPlanVersionId and if the report is marked as final.
- See TestPlanReport type for more information.
- """
- testPlanReports(
- testPlanVersionPhases: [TestPlanVersionPhase]
- testPlanVersionId: ID
- testPlanVersionIds: [ID]
- atId: ID
- isFinal: Boolean
- ): [TestPlanReport]!
- """
- Get a TestPlanReport by ID.
- """
- testPlanReport(id: ID!): TestPlanReport
- """
- Get a TestPlanRun by ID.
- """
- testPlanRun(id: ID!): TestPlanRun
- """
- Get all TestPlanRuns.
- """
- testPlanRuns(testPlanReportId: ID): [TestPlanRun]!
- """
- For a given ID, load all the associated data which can be inferred from
- that ID. For more information, take a look at the description of the
- LocationOfDatInput type.
- """
- populateData(locationOfData: LocationOfDataInput!): PopulatedData!
- """
- Get a CollectionJob by ID.
- """
- collectionJob(id: ID!): CollectionJob
- """
- Get a CollectionJob by TestPlanRun ID.
- """
- collectionJobByTestPlanRunId(testPlanRunId: ID!): CollectionJob
- """
- Get all CollectionJobs.
- """
- collectionJobs: [CollectionJob]!
- }
-
- # Mutation-specific types below
-
- """
- Mutations scoped to an Assistive Technology.
- """
- type AtOperations {
- """
- Get an AtVersion or create it if it does not exist. In the case the
- AtVersion already exists, the releasedAt field will be ignored.
- """
- findOrCreateAtVersion(input: AtVersionInput!): AtVersion!
- }
-
- """
- Mutations scoped to an existing AtVersion.
- """
- type AtVersionOperations {
- """
- Edit the version.
- """
- updateAtVersion(input: AtVersionInput!): AtVersion!
- """
- Delete an unused AtVersion. If it is in use by any TestResults, the
- AtVersion will not be deleted and the array of offending TestResults
- will be returned in the response.
- """
- deleteAtVersion: DeleteAtVersionResult!
- }
-
- type DeleteAtVersionResult {
- """
- A boolean which will be true if the AtVersion was deleted.
- """
- isDeleted: Boolean!
- """
- An array of TestResults which are using the AtVersion and have therefore
- prevented its deletion. There is a check in place to limit the number of
- queries this endpoint will make, so in an extreme case the list may not
- be exhaustive.
- """
- failedDueToTestResults: [PopulatedData]
- }
-
- """
- Mutations scoped to a browser.
- """
- type BrowserOperations {
- """
- Get a BrowserVersion or create it if it does not exist.
- """
- findOrCreateBrowserVersion(input: BrowserVersionInput!): BrowserVersion!
- }
-
- """
- Mutations scoped to a previously-created TestPlanReport.
- """
- type TestPlanReportOperations {
- """
- Assigns a user to a TestPlanReport, if a testPlanRunID is supplied the
- the TestPlanRun will be reassigned, otherwise an associated TestPlanRun
- with no results is created.
- """
- assignTester(userId: ID!, testPlanRunId: ID): PopulatedData!
- """
- Permanently deletes the TestPlanRun from the TestPlanReport for the
- user.
- """
- deleteTestPlanRun(userId: ID!): PopulatedData!
- """
- 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.
-
- Also optionally set a "primary test plan run" so a specific tester's output
- will be shown for on the report pages over another.
- """
- markAsFinal(primaryTestPlanRunId: ID): 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.
- """
- unmarkAsFinal: PopulatedData!
- """
- Move the vendor review status from READY to IN PROGRESS
- or IN PROGRESS to APPROVED
- """
- promoteVendorReviewStatus(vendorReviewStatus: String!): PopulatedData
- """
- Permanently deletes the TestPlanReport and all associated TestPlanRuns.
- Only available to admins.
- """
- deleteTestPlanReport: NoResponse
- }
-
- """
- Mutations scoped to a previously-created TestPlanVersion.
- """
- type TestPlanVersionOperations {
- """
- Update the test plan version phase. Remember that all conflicts must be resolved
- 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.
- Only available to admins.
- """
- updateRecommendedPhaseTargetDate(
- recommendedPhaseTargetDate: Timestamp!
- ): PopulatedData!
- }
-
- """
- Mutations scoped to a previously-created TestPlanRun.
- """
- type TestPlanRunOperations {
- """
- Creates a TestResult which is populated with all the ScenarioResults
- and AssertionResults to be filled out for the AT associated with the
- TestPlanRun, or returns the already existing TestResult. In the case
- that the TestResult already exists, the atVersionId and browserVersionId
- will be ignored.
- """
- findOrCreateTestResult(
- testId: ID!
- atVersionId: ID!
- browserVersionId: ID!
- ): PopulatedData!
- """
- Permanently deletes all test results without removing the TestPlanRun.
- """
- deleteTestResults: PopulatedData!
- }
-
- """
- Mutations scoped to a previously-created TestResult.
- """
- type TestResultOperations {
- """
- Saves any changes to the TestResult. Minimal validation is performed
- since this mutation is meant to be called repeatedly as the user
- completes the forms.
- """
- saveTestResult(input: TestResultInput!): PopulatedData!
- """
- Should only be called when the TestResult is complete, with all
- ScenarioResults and AssertionResults filled in, or else this
- mutation will throw an error. This endpoint will set the completedAt
- field, indicating the test has been successfully submitted and accepted.
- """
- submitTestResult(input: TestResultInput!): PopulatedData!
- """
- Permanently deletes the TestResult.
- """
- deleteTestResult: PopulatedData!
- }
- """
- Mutations scoped to a CollectionJob.
- """
- type CollectionJobOperations {
- """
- Mark a CollectionJob as finished.
- """
- cancelCollectionJob: CollectionJob!
- """
- Retry the 'cancelled' tests of a CollectionJob.
- """
- retryCanceledCollections: CollectionJob!
- }
-
- type Mutation {
- """
- Get the available mutations for the given AT.
- """
- at(id: ID!): AtOperations!
- """
- Get the available mutations for the given AT version.
- """
- atVersion(id: ID!): AtVersionOperations!
- """
- Get the available mutations for the given browser.
- """
- browser(id: ID!): BrowserOperations!
- """
- Adds an empty report to the test queue, a container for related test
- results. Each report must be scoped to a specific TestPlanVersion, AT
- and Browser. Optionally, either a minimum or exact AT version
- requirement can be included to constrain the versions testers are
- allowed to use to run the tests.
- """
- createTestPlanReport(
- """
- The TestPlanReport to create.
- """
- input: TestPlanReportInput!
- ): PopulatedData!
- """
- Get the available mutations for the given TestPlanReport.
- """
- testPlanReport(id: ID, ids: [ID]): TestPlanReportOperations!
- """
- Get the available mutations for the given TestPlanRun.
- """
- testPlanRun(id: ID!): TestPlanRunOperations!
- """
- Get the available mutations for the given TestResult.
- """
- testResult(id: ID!): TestResultOperations!
- """
- Get the available mutations for the given TestPlanVersion.
- """
- testPlanVersion(id: ID!): TestPlanVersionOperations!
- """
- Get the available mutations for the given CollectionJob.
- """
- collectionJob(id: ID!): CollectionJobOperations
- """
- Update the currently-logged-in User.
- """
- updateMe(input: UserInput): User!
- """
- Add a viewer to a test
- """
- addViewer(testPlanVersionId: ID!, testId: ID!): User!
- """
- Schedule a new CollectionJob through the Response Scheduler
- """
- scheduleCollectionJob(
- """
- The CollectionJob to schedule.
- """
- testPlanReportId: ID!
- ): CollectionJob!
- """
- Update a CollectionJob
- """
- updateCollectionJob(
- """
- The CollectionJob to update.
- """
- id: ID!
- """
- The status of the CollectionJob.
- """
- status: CollectionJobStatus
- """
- The external logs url of the CollectionJob.
- """
- externalLogsUrl: String
- ): CollectionJob
- """
- Restart a CollectionJob by way of the Response Scheduler
- """
- restartCollectionJob(
- """
- The CollectionJob to restart.
- """
- id: ID!
- ): CollectionJob
- """
- Delete a CollectionJob
- """
- deleteCollectionJob(id: ID!): NoResponse!
- }
+ feedbackType: IssueFeedbackType!
+ """
+ Indicates if the issue is currently open on GitHub.
+ """
+ isOpen: Boolean!
+ """
+ Test Number the issue was raised for.
+ """
+ testNumberFilteredByAt: Int
+ """
+ The time the issue was created, according to GitHub.
+ """
+ createdAt: Timestamp!
+ """
+ The time the issue was closed, if it was closed.
+ """
+ closedAt: Timestamp
+ """
+ The AT associated with the issue. Although there are not currently any
+ cases where we generate GitHub issues without an associated AT, that
+ may not remain true forever and we do support this field being
+ undefined.
+ """
+ at: At
+ """
+ The browser associated with the issue, which may not be present.
+ """
+ browser: Browser
+ }
+
+ """
+ A container for test results as captured by multiple testers. The tests to
+ be run for a TestPlanReport originate in the TestPlanVersion.
+ """
+ type TestPlanReport {
+ """
+ Postgres-provided numeric ID.
+ """
+ id: ID!
+ """
+ The snapshot of a TestPlan to use.
+ """
+ testPlanVersion: TestPlanVersion!
+ """
+ The AT used when collecting results.
+ """
+ at: At!
+ """
+ Either a minimumAtVersion or exactAtVersion will be available. The
+ minimumAtVersion, when defined, is the oldest version of the AT that
+ testers are allowed to use when collecting results.
+ """
+ minimumAtVersion: AtVersion
+ """
+ Either a minimumAtVersion or exactAtVersion will be available. The
+ exactAtVersion, when defined, is the only version of the AT that
+ testers are allowed to use when collecting results. Note that when a
+ TestPlanVersion reaches the recommended stage, all its reports will
+ automatically switch from having a minimumAtVersion to an
+ exactAtVersion. See the earliestAtVersion field of TestPlanVersion for
+ more information.
+ """
+ exactAtVersion: AtVersion
+ """
+ The unique AT Versions used when collecting results for this report.
+ """
+ atVersions: [AtVersion]!
+ """
+ The latest AT Version used collecting results for this report.
+ """
+ latestAtVersionReleasedAt: AtVersion
+ """
+ The browser used when collecting results.
+ """
+ browser: Browser!
+ """
+ The subset of tests which are relevant to this report, i.e. the tests
+ where the AT matches the report's AT.
+ """
+ runnableTests: [Test]!
+ """
+ The number of tests available for this TestPlanReport's AT. Foregoes the
+ need of getting the length from runnableTests which would require
+ running expensive time-consuming operations to calculate.
+ """
+ runnableTestsLength: Int!
+ """
+ A list of conflicts between runs, which may occur at the level of the
+ 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 TestPlanVersion phase can change from
+ DRAFT to CANDIDATE.
+ """
+ conflicts: [TestPlanReportConflict]!
+ """
+ The number of conflicts for this TestPlanReport. Foregoes the need for
+ getting the length from conflicts which would require running expensive
+ time-consuming operations to calculate.
+ """
+ conflictsLength: Int!
+ """
+ Finalizing a test plan report requires resolving any conflicts between
+ runs. At this stage a single set of results is able to represent all
+ results, and is much more convenient to work with.
+ """
+ finalizedTestResults: [TestResult]
+ """
+ A list of all issues which have filed through "Raise an Issue" buttons
+ in the app. Note that results will be cached for at least ten seconds.
+ """
+ issues: [Issue]!
+ """
+ These are all the TestPlanRuns which were recorded during the
+ TestPlanReport's DRAFT stage.
+ """
+ draftTestPlanRuns: [TestPlanRun]!
+ """
+ The state of the vendor review, which can be "READY", "IN_PROGRESS", and "APPROVED"
+ """
+ vendorReviewStatus: String
+ """
+ Various metrics and calculations related to the TestPlanReport which
+ may be used for reporting purposes.
+ """
+ metrics: Any!
+ """
+ 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!
+ """
+ The AtVersion to display for a TestPlanReport only when the
+ TestPlanVersion is RECOMMENDED.
+
+ If this TestPlanReport was created with an "exactAtVersionId" being set,
+ it will use the matching AtVersion, otherwise it will use the
+ TestPlanVersion.earliestAtVersion as a default.
+ """
+ recommendedAtVersion: AtVersion
+ }
+
+ """
+ Minimal plain representation of a TestPlanReport.
+ """
+ input TestPlanReportInput {
+ testPlanVersionId: ID!
+ atId: ID!
+ exactAtVersionId: ID
+ minimumAtVersionId: ID
+ browserId: ID!
+ copyResultsFromTestPlanVersionId: ID
+ }
+
+ """
+ Allows you to provide an ID, any ID, and load all the data which
+ can be found through relationships to that ID. For example, if you have a
+ scenarioResultId, it is possible to find a ScenarioResult, Scenario,
+ TestResult, Test, TestPlanVersion, TestPlanTarget, At, AtVersion, Browser,
+ BrowserVersion, and TestPlan. This can save a lot of effort wrangling data!
+ """
+ input LocationOfDataInput {
+ testPlanId: ID
+ testPlanVersionId: ID
+ testId: ID
+ scenarioId: ID
+ assertionId: ID
+ testPlanReportId: ID
+ browserId: ID
+ browserVersionId: ID
+ atId: ID
+ atVersionId: ID
+ testPlanRunId: ID
+ testResultId: ID
+ scenarioResultId: ID
+ assertionResultId: ID
+ }
+ """
+ Contains the same fields as the LocationOfDataInput type. This is used to
+ return a LocationOfData in queries in the same form you can provide it as an
+ input type. It is a scalar because otherwise you would need to request every
+ single field, which would be hugely inconvenient.
+ """
+ scalar LocationOfData
+
+ """
+ The fully-populated data which is associated with a given LocationOfData.
+ For example, a LocationOfData which includes an ID for a TestPlanReport
+ would allow you to populate the TestPlanVersion, AT, Browser and TestPlan,
+ which are knowable through relationships to that TestPlanReport.
+ """
+ type PopulatedData {
+ locationOfData: LocationOfData!
+ testPlan: TestPlan
+ testPlanVersion: TestPlanVersion
+ at: At
+ atVersion: AtVersion
+ browser: Browser
+ browserVersion: BrowserVersion
+ test: Test
+ scenario: Scenario
+ assertion: Assertion
+ testPlanReport: TestPlanReport
+ testPlanRun: TestPlanRun
+ testResult: TestResult
+ scenarioResult: ScenarioResult
+ assertionResult: AssertionResult
+ }
+
+ type Query {
+ """
+ Get the currently-logged-in user or null if you are not logged in.
+ """
+ me: User
+ """
+ Get all registered users. Must be logged in.
+ """
+ users: [User]!
+ """
+ Get all assistive technologies known to the app.
+ """
+ ats: [At]!
+ """
+ Get all browsers known to the app.
+ """
+ browsers: [Browser]!
+ """
+ Get all TestPlans.
+ """
+ testPlans(testPlanVersionPhases: [TestPlanVersionPhase]): [TestPlan]!
+ """
+ Load a particular TestPlan by ID.
+ """
+ testPlan(id: ID!): TestPlan
+ """
+ Get all TestPlanVersions.
+ """
+ testPlanVersions(
+ phases: [TestPlanVersionPhase]
+ directory: String
+ ): [TestPlanVersion]!
+ """
+ Get a particular TestPlanVersion by ID.
+ """
+ testPlanVersion(id: ID): TestPlanVersion
+ """
+ Load multiple TestPlanReports, with the optional ability to filter by
+ TestPlanVersionPhase, atId, testPlanVersionId and if the report is marked as final.
+ See TestPlanReport type for more information.
+ """
+ testPlanReports(
+ testPlanVersionPhases: [TestPlanVersionPhase]
+ testPlanVersionId: ID
+ testPlanVersionIds: [ID]
+ atId: ID
+ isFinal: Boolean
+ ): [TestPlanReport]!
+ """
+ Get a TestPlanReport by ID.
+ """
+ testPlanReport(id: ID!): TestPlanReport
+ """
+ Get a TestPlanRun by ID.
+ """
+ testPlanRun(id: ID!): TestPlanRun
+ """
+ Get all TestPlanRuns.
+ """
+ testPlanRuns(testPlanReportId: ID): [TestPlanRun]!
+ """
+ For a given ID, load all the associated data which can be inferred from
+ that ID. For more information, take a look at the description of the
+ LocationOfDatInput type.
+ """
+ populateData(locationOfData: LocationOfDataInput!): PopulatedData!
+ """
+ Get a CollectionJob by ID.
+ """
+ collectionJob(id: ID!): CollectionJob
+ """
+ Get a CollectionJob by TestPlanRun ID.
+ """
+ collectionJobByTestPlanRunId(testPlanRunId: ID!): CollectionJob
+ """
+ Get all CollectionJobs.
+ """
+ collectionJobs: [CollectionJob]!
+ }
+
+ # Mutation-specific types below
+
+ """
+ Mutations scoped to an Assistive Technology.
+ """
+ type AtOperations {
+ """
+ Get an AtVersion or create it if it does not exist. In the case the
+ AtVersion already exists, the releasedAt field will be ignored.
+ """
+ findOrCreateAtVersion(input: AtVersionInput!): AtVersion!
+ }
+
+ """
+ Mutations scoped to an existing AtVersion.
+ """
+ type AtVersionOperations {
+ """
+ Edit the version.
+ """
+ updateAtVersion(input: AtVersionInput!): AtVersion!
+ """
+ Delete an unused AtVersion. If it is in use by any TestResults, the
+ AtVersion will not be deleted and the array of offending TestResults
+ will be returned in the response.
+ """
+ deleteAtVersion: DeleteAtVersionResult!
+ }
+
+ type DeleteAtVersionResult {
+ """
+ A boolean which will be true if the AtVersion was deleted.
+ """
+ isDeleted: Boolean!
+ """
+ An array of TestResults which are using the AtVersion and have therefore
+ prevented its deletion. There is a check in place to limit the number of
+ queries this endpoint will make, so in an extreme case the list may not
+ be exhaustive.
+ """
+ failedDueToTestResults: [PopulatedData]
+ }
+
+ """
+ Mutations scoped to a browser.
+ """
+ type BrowserOperations {
+ """
+ Get a BrowserVersion or create it if it does not exist.
+ """
+ findOrCreateBrowserVersion(input: BrowserVersionInput!): BrowserVersion!
+ }
+
+ """
+ Mutations scoped to a previously-created TestPlanReport.
+ """
+ type TestPlanReportOperations {
+ """
+ Assigns a user to a TestPlanReport, if a testPlanRunID is supplied the
+ the TestPlanRun will be reassigned, otherwise an associated TestPlanRun
+ with no results is created.
+ """
+ assignTester(userId: ID!, testPlanRunId: ID): PopulatedData!
+ """
+ Permanently deletes the TestPlanRun from the TestPlanReport for the
+ user.
+ """
+ deleteTestPlanRun(userId: ID!): PopulatedData!
+ """
+ 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.
+
+ Also optionally set a "primary test plan run" so a specific tester's output
+ will be shown for on the report pages over another.
+ """
+ markAsFinal(primaryTestPlanRunId: ID): 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.
+ """
+ unmarkAsFinal: PopulatedData!
+ """
+ Move the vendor review status from READY to IN PROGRESS
+ or IN PROGRESS to APPROVED
+ """
+ promoteVendorReviewStatus(vendorReviewStatus: String!): PopulatedData
+ """
+ Permanently deletes the TestPlanReport and all associated TestPlanRuns.
+ Only available to admins.
+ """
+ deleteTestPlanReport: NoResponse
+ }
+
+ """
+ Mutations scoped to a previously-created TestPlanVersion.
+ """
+ type TestPlanVersionOperations {
+ """
+ Update the test plan version phase. Remember that all conflicts must be resolved
+ 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.
+ Only available to admins.
+ """
+ updateRecommendedPhaseTargetDate(
+ recommendedPhaseTargetDate: Timestamp!
+ ): PopulatedData!
+ }
+
+ """
+ Mutations scoped to a previously-created TestPlanRun.
+ """
+ type TestPlanRunOperations {
+ """
+ Creates a TestResult which is populated with all the ScenarioResults
+ and AssertionResults to be filled out for the AT associated with the
+ TestPlanRun, or returns the already existing TestResult. In the case
+ that the TestResult already exists, the atVersionId and browserVersionId
+ will be ignored.
+ """
+ findOrCreateTestResult(
+ testId: ID!
+ atVersionId: ID!
+ browserVersionId: ID!
+ ): PopulatedData!
+ """
+ Permanently deletes all test results without removing the TestPlanRun.
+ """
+ deleteTestResults: PopulatedData!
+ }
+
+ """
+ Mutations scoped to a previously-created TestResult.
+ """
+ type TestResultOperations {
+ """
+ Saves any changes to the TestResult. Minimal validation is performed
+ since this mutation is meant to be called repeatedly as the user
+ completes the forms.
+ """
+ saveTestResult(input: TestResultInput!): PopulatedData!
+ """
+ Should only be called when the TestResult is complete, with all
+ ScenarioResults and AssertionResults filled in, or else this
+ mutation will throw an error. This endpoint will set the completedAt
+ field, indicating the test has been successfully submitted and accepted.
+ """
+ submitTestResult(input: TestResultInput!): PopulatedData!
+ """
+ Permanently deletes the TestResult.
+ """
+ deleteTestResult: PopulatedData!
+ }
+ """
+ Mutations scoped to a CollectionJob.
+ """
+ type CollectionJobOperations {
+ """
+ Mark a CollectionJob as finished.
+ """
+ cancelCollectionJob: CollectionJob!
+ """
+ Retry the 'cancelled' tests of a CollectionJob.
+ """
+ retryCanceledCollections: CollectionJob!
+ }
+
+ type Mutation {
+ """
+ Get the available mutations for the given AT.
+ """
+ at(id: ID!): AtOperations!
+ """
+ Get the available mutations for the given AT version.
+ """
+ atVersion(id: ID!): AtVersionOperations!
+ """
+ Get the available mutations for the given browser.
+ """
+ browser(id: ID!): BrowserOperations!
+ """
+ Adds an empty report to the test queue, a container for related test
+ results. Each report must be scoped to a specific TestPlanVersion, AT
+ and Browser. Optionally, either a minimum or exact AT version
+ requirement can be included to constrain the versions testers are
+ allowed to use to run the tests.
+ """
+ createTestPlanReport(
+ """
+ The TestPlanReport to create.
+ """
+ input: TestPlanReportInput!
+ ): PopulatedData!
+ """
+ Get the available mutations for the given TestPlanReport.
+ """
+ testPlanReport(id: ID, ids: [ID]): TestPlanReportOperations!
+ """
+ Get the available mutations for the given TestPlanRun.
+ """
+ testPlanRun(id: ID!): TestPlanRunOperations!
+ """
+ Get the available mutations for the given TestResult.
+ """
+ testResult(id: ID!): TestResultOperations!
+ """
+ Get the available mutations for the given TestPlanVersion.
+ """
+ testPlanVersion(id: ID!): TestPlanVersionOperations!
+ """
+ Get the available mutations for the given CollectionJob.
+ """
+ collectionJob(id: ID!): CollectionJobOperations
+ """
+ Update the currently-logged-in User.
+ """
+ updateMe(input: UserInput): User!
+ """
+ Add a viewer to a test
+ """
+ addViewer(testPlanVersionId: ID!, testId: ID!): User!
+ """
+ Schedule a new CollectionJob through the Response Scheduler
+ """
+ scheduleCollectionJob(
+ """
+ The CollectionJob to schedule.
+ """
+ testPlanReportId: ID!
+ ): CollectionJob!
+ """
+ Update a CollectionJob
+ """
+ updateCollectionJob(
+ """
+ The CollectionJob to update.
+ """
+ id: ID!
+ """
+ The status of the CollectionJob.
+ """
+ status: CollectionJobStatus
+ """
+ The external logs url of the CollectionJob.
+ """
+ externalLogsUrl: String
+ ): CollectionJob
+ """
+ Restart a CollectionJob by way of the Response Scheduler
+ """
+ restartCollectionJob(
+ """
+ The CollectionJob to restart.
+ """
+ id: ID!
+ ): CollectionJob
+ """
+ Delete a CollectionJob
+ """
+ deleteCollectionJob(id: ID!): NoResponse!
+ }
`;
module.exports = graphqlSchema;
diff --git a/server/graphql-server.js b/server/graphql-server.js
index f7e87a398..4ab29c9ba 100644
--- a/server/graphql-server.js
+++ b/server/graphql-server.js
@@ -1,17 +1,17 @@
const { ApolloServer } = require('apollo-server-express');
const {
- ApolloServerPluginLandingPageGraphQLPlayground
+ ApolloServerPluginLandingPageGraphQLPlayground
} = require('apollo-server-core');
const graphqlSchema = require('./graphql-schema');
const getGraphQLContext = require('./graphql-context');
const resolvers = require('./resolvers');
const apolloServer = new ApolloServer({
- typeDefs: graphqlSchema,
- context: getGraphQLContext,
- resolvers,
- // The newer IDE does not work because of CORS issues
- plugins: [ApolloServerPluginLandingPageGraphQLPlayground()]
+ typeDefs: graphqlSchema,
+ context: getGraphQLContext,
+ resolvers,
+ // The newer IDE does not work because of CORS issues
+ plugins: [ApolloServerPluginLandingPageGraphQLPlayground()]
});
module.exports = apolloServer;
diff --git a/server/handlebars/embed/helpers/index.js b/server/handlebars/embed/helpers/index.js
index 5886d2528..8904dfc88 100644
--- a/server/handlebars/embed/helpers/index.js
+++ b/server/handlebars/embed/helpers/index.js
@@ -1,29 +1,29 @@
module.exports = {
- dataEmpty: function (object) {
- return object.length === 0;
- },
+ dataEmpty: function (object) {
+ return object.length === 0;
+ },
- isCandidate: function (value) {
- return value === 'CANDIDATE';
- },
- getMustSupportData: function (object) {
- return Math.trunc(
- (object.metrics.mustAssertionsPassedCount /
- object.metrics.mustAssertionsCount) *
- 100
- );
- },
- isMustAssertionPriority: function (object) {
- return object.metrics.mustAssertionsCount > 0;
- },
- isShouldAssertionPriority: function (object) {
- return object.metrics.shouldAssertionsCount > 0;
- },
- getShouldSupportData: function (object) {
- return Math.trunc(
- (object.metrics.shouldAssertionsPassedCount /
- object.metrics.shouldAssertionsCount) *
- 100
- );
- }
+ isCandidate: function (value) {
+ return value === 'CANDIDATE';
+ },
+ getMustSupportData: function (object) {
+ return Math.trunc(
+ (object.metrics.mustAssertionsPassedCount /
+ object.metrics.mustAssertionsCount) *
+ 100
+ );
+ },
+ isMustAssertionPriority: function (object) {
+ return object.metrics.mustAssertionsCount > 0;
+ },
+ isShouldAssertionPriority: function (object) {
+ return object.metrics.shouldAssertionsCount > 0;
+ },
+ getShouldSupportData: function (object) {
+ return Math.trunc(
+ (object.metrics.shouldAssertionsPassedCount /
+ object.metrics.shouldAssertionsCount) *
+ 100
+ );
+ }
};
diff --git a/server/handlebars/embed/public/script.js b/server/handlebars/embed/public/script.js
index 198aec4b9..8e6fc81d9 100644
--- a/server/handlebars/embed/public/script.js
+++ b/server/handlebars/embed/public/script.js
@@ -1,9 +1,9 @@
const iframeClass = `support-levels-${
- document.location.href.match(/([^/]+)\/?$/)?.[1]
+ document.location.href.match(/([^/]+)\/?$/)?.[1]
}`;
const iframeCode = link =>
- `