diff --git a/.eslintrc.json b/.eslintrc.json
index 774709ebc..522b0df03 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,4 +1,9 @@
{
+ "ignorePatterns": [
+ "client/dist/*",
+ "client/dist/bundle.js",
+ "client/tests/e2e/snapshots/saved"
+ ],
"env": {
"browser": true,
"es6": true,
@@ -19,40 +24,19 @@
"ecmaVersion": 2020,
"sourceType": "module"
},
- "plugins": [
- "react",
- "json",
- "prettier",
- "jest"
- ],
+ "plugins": ["react", "json", "prettier", "jest"],
"rules": {
- "linebreak-style": [
- "error",
- "unix"
- ],
- "semi": [
- "error",
- "always"
- ],
- "eol-last": [
- "error",
- "always"
- ],
+ "linebreak-style": ["error", "unix"],
+ "semi": ["error", "always"],
+ "eol-last": ["error", "always"],
"no-console": [
"error",
{
- "allow": [
- "warn",
- "error"
- ]
+ "allow": ["warn", "error"]
}
],
- "no-use-before-define": [
- "off"
- ],
- "react/display-name": [
- "off"
- ]
+ "no-use-before-define": ["off"],
+ "react/display-name": ["off"]
},
"settings": {
"react": {
diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml
index 4a15cf1b8..681973cce 100644
--- a/.github/workflows/deploy-production.yml
+++ b/.github/workflows/deploy-production.yml
@@ -22,7 +22,7 @@ jobs:
python-version: '3.10'
- name: Install ansible and deploy to production
run: |
- python -m pip install --user ansible-core==2.11.1
+ python -m pip install --user ansible-core==2.16.14
cd deploy
echo ${{ secrets.ANSIBLE_VAULT_PASSWORD }} > ansible-vault-password.txt
ansible-vault view --vault-password-file ansible-vault-password.txt files/jwt-signing-key.pem.enc > ../jwt-signing-key.pem
diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml
index a88d06939..563c5e375 100644
--- a/.github/workflows/deploy-staging.yml
+++ b/.github/workflows/deploy-staging.yml
@@ -22,7 +22,7 @@ jobs:
python-version: '3.10'
- name: Install ansible and deploy to staging
run: |
- python -m pip install --user ansible-core==2.11.1
+ python -m pip install --user ansible-core==2.16.14
cd deploy
echo ${{ secrets.ANSIBLE_VAULT_PASSWORD }} > ansible-vault-password.txt
ansible-vault view --vault-password-file ansible-vault-password.txt files/jwt-signing-key.pem.enc > ../jwt-signing-key.pem
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 000000000..2312dc587
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+npx lint-staged
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 000000000..a778dc2fa
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,5 @@
+client/dist/*
+client/dist/bundle.js
+
+# snaphosts
+client/tests/e2e/snapshots/saved
diff --git a/client/.eslintignore b/client/.eslintignore
deleted file mode 100644
index 318b1cc8a..000000000
--- a/client/.eslintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-dist/*
-dist/bundle.js
\ No newline at end of file
diff --git a/client/components/ManageBotRunDialog/index.jsx b/client/components/ManageBotRunDialog/index.jsx
index 8915ad55b..deb7b9163 100644
--- a/client/components/ManageBotRunDialog/index.jsx
+++ b/client/components/ManageBotRunDialog/index.jsx
@@ -17,6 +17,7 @@ import RetryCanceledCollectionsButton from './RetryCanceledCollectionsButton';
import StopRunningCollectionButton from './StopRunningCollectionButton';
import ViewLogsButton from './ViewLogsButton';
import { TestPlanRunPropType, UserPropType } from '../common/proptypes';
+import { COLLECTION_JOB_STATUS } from '../../utils/collectionJobStatus';
const ManageBotRunDialog = ({
testPlanReportId,
@@ -68,6 +69,19 @@ const ManageBotRunDialog = ({
[testers, testPlanReportAssignedTestersQuery]
);
+ const isBotRunFinished = useMemo(() => {
+ const status = collectionJobQuery?.collectionJobByTestPlanRunId?.status;
+ if (!status) return false;
+ switch (status) {
+ case COLLECTION_JOB_STATUS.COMPLETED:
+ case COLLECTION_JOB_STATUS.ERROR:
+ case COLLECTION_JOB_STATUS.CANCELLED:
+ return true;
+ default:
+ return false;
+ }
+ }, [collectionJobQuery]);
+
const actions = useMemo(() => {
return [
{
@@ -77,6 +91,7 @@ const ManageBotRunDialog = ({
testPlanRun: testPlanRun,
possibleTesters: possibleReassignees,
label: 'Assign To ...',
+ disabled: !isBotRunFinished,
onChange
}
},
diff --git a/client/components/Reports/SummarizeTestPlanReport.jsx b/client/components/Reports/SummarizeTestPlanReport.jsx
index e71db42ac..a7e20b0e0 100644
--- a/client/components/Reports/SummarizeTestPlanReport.jsx
+++ b/client/components/Reports/SummarizeTestPlanReport.jsx
@@ -263,6 +263,7 @@ const SummarizeTestPlanReport = ({ testPlanVersion, testPlanReports }) => {
atVersionName: testResult.atVersion.name,
browserName: testPlanReport.browser.name,
browserVersionName: testResult.browserVersion.name,
+ versionPhase: testPlanVersion.phase,
reportLink
});
diff --git a/client/components/SortableIssuesTable/index.jsx b/client/components/SortableIssuesTable/index.jsx
index 3ed20a2a7..f70f6f408 100644
--- a/client/components/SortableIssuesTable/index.jsx
+++ b/client/components/SortableIssuesTable/index.jsx
@@ -24,7 +24,7 @@ const SORT_FIELDS = {
CLOSED_AT: 'closedAt'
};
-const SortableIssuesTable = ({ issues }) => {
+const SortableIssuesTable = ({ issues, issueLink }) => {
const [activeSort, setActiveSort] = useState(SORT_FIELDS.STATUS);
const [sortOrder, setSortOrder] = useState(TABLE_SORT_ORDERS.ASC);
const [activeFilter, setActiveFilter] = useState('OPEN');
@@ -187,12 +187,20 @@ const SortableIssuesTable = ({ issues }) => {
{renderTableBody()}
)}
+ {issueLink && (
+
+ )}
>
);
};
SortableIssuesTable.propTypes = {
- issues: PropTypes.arrayOf(IssuePropType).isRequired
+ issues: PropTypes.arrayOf(IssuePropType).isRequired,
+ issueLink: PropTypes.string
};
export default SortableIssuesTable;
diff --git a/client/components/TestRenderer/index.jsx b/client/components/TestRenderer/index.jsx
index 6eb15ab0d..97042d48f 100644
--- a/client/components/TestRenderer/index.jsx
+++ b/client/components/TestRenderer/index.jsx
@@ -27,6 +27,7 @@ import UnexpectedBehaviorsFieldset from './UnexpectedBehaviorsFieldset';
import supportJson from '../../resources/support.json';
import commandsJson from '../../resources/commands.json';
import { AtPropType, TestResultPropType } from '../common/proptypes/index.js';
+import createIssueLink from '@client/utils/createIssueLink';
const Container = styled.div`
width: 100%;
@@ -163,7 +164,8 @@ const TestRenderer = ({
isReviewingBot = false,
isReadOnly = false,
isEdit = false,
- setIsRendererReady = false
+ setIsRendererReady = false,
+ commonIssueContent
}) => {
const { scenarioResults, test = {}, completedAt } = testResult;
const { renderableContent } = test;
@@ -498,7 +500,7 @@ const TestRenderer = ({
{mayAssertionsFailedCount} unsupported)
>
@@ -543,6 +545,13 @@ const TestRenderer = ({
unexpectedBehaviors,
assertionsHeader
} = value;
+
+ const commandString = header.replace('After ', '');
+ const issueLink = createIssueLink({
+ ...commonIssueContent,
+ commandString
+ });
+
return (
{header}
@@ -564,6 +573,9 @@ const TestRenderer = ({
isSubmitted={isSubmitted}
readOnly={isReadOnly}
/>
+
+ Raise an issue for {commandString}
+
);
})}
@@ -604,7 +616,8 @@ TestRenderer.propTypes = {
isReadOnly: PropTypes.bool,
isEdit: PropTypes.bool,
isReviewingBot: PropTypes.bool,
- setIsRendererReady: PropTypes.func
+ setIsRendererReady: PropTypes.func,
+ commonIssueContent: PropTypes.object
};
export default TestRenderer;
diff --git a/client/components/TestReview/index.jsx b/client/components/TestReview/index.jsx
index 93fad9883..7d2f53300 100644
--- a/client/components/TestReview/index.jsx
+++ b/client/components/TestReview/index.jsx
@@ -2,7 +2,7 @@ import React, { Fragment, useMemo, useState } from 'react';
import { useQuery } from '@apollo/client';
import { TEST_REVIEW_PAGE_QUERY } from './queries';
import { Container } from 'react-bootstrap';
-import { Link, useParams } from 'react-router-dom';
+import { Link, useLocation, useParams } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import PageStatus from '../common/PageStatus';
import InstructionsRenderer from '../CandidateReview/CandidateTestPlanRun/InstructionsRenderer';
@@ -12,6 +12,7 @@ import { derivePhaseName } from '../../utils/aria';
import { dates } from 'shared';
import supportJson from '../../resources/support.json';
import SortableIssuesTable from '../SortableIssuesTable';
+import createIssueLink from '../../utils/createIssueLink';
const Ul = styled.ul`
li {
@@ -28,6 +29,7 @@ const FilterButtonContainer = styled.div`
`;
const TestReview = () => {
+ const location = useLocation();
const { testPlanVersionId } = useParams();
const { loading, data, error } = useQuery(TEST_REVIEW_PAGE_QUERY, {
@@ -237,7 +239,16 @@ const TestReview = () => {
}
)}
-
+
Tests
@@ -145,7 +148,8 @@ const TestRunHeading = ({
data-testid="apg-example-name"
>
@@ -177,6 +181,8 @@ const TestRunHeading = ({
TestRunHeading.propTypes = {
testPlanTitle: PropTypes.string.isRequired,
+ testPlanVersionString: PropTypes.string.isRequired,
+ testPlanVersionReviewLink: PropTypes.string.isRequired,
at: PropTypes.string.isRequired,
browser: PropTypes.string.isRequired,
showEditAtBrowser: PropTypes.bool.isRequired,
diff --git a/client/components/TestRun/index.jsx b/client/components/TestRun/index.jsx
index b6ca26d0c..3112497a1 100644
--- a/client/components/TestRun/index.jsx
+++ b/client/components/TestRun/index.jsx
@@ -39,7 +39,6 @@ import { evaluateAuth } from '../../utils/evaluateAuth';
import './TestRun.css';
import ReviewConflicts from '../ReviewConflicts';
import createIssueLink from '../../utils/createIssueLink';
-import { dates } from 'shared';
import { Provider as CollectionJobContextProvider } from './CollectionJobContext';
import { useUrlTestIndex } from '../../hooks/useUrlTestIndex';
@@ -360,16 +359,13 @@ const TestRun = () => {
}
adminReviewerCheckedRef.current = true;
- let issueLink;
+ let issueLink, commonIssueContent;
const hasLoadingCompleted = Object.keys(currentTest).length;
if (hasLoadingCompleted) {
- issueLink = createIssueLink({
+ commonIssueContent = {
testPlanTitle: testPlanVersion.title,
testPlanDirectory: testPlanVersion.testPlan.directory,
- versionString: `V${dates.convertDateToString(
- testPlanVersion.updatedAt,
- 'YY.MM.DD'
- )}`,
+ versionString: testPlanVersion.versionString,
testTitle: currentTest.title,
testRowNumber: currentTest.rowNumber,
testSequenceNumber: currentTest.seq,
@@ -379,7 +375,8 @@ const TestRun = () => {
atVersionName: currentAtVersion?.name,
browserVersionName: currentBrowserVersion?.name,
conflictMarkdown: conflictMarkdownRef.current
- });
+ };
+ issueLink = createIssueLink(commonIssueContent);
}
const remapScenarioResults = (
@@ -969,7 +966,7 @@ const TestRun = () => {
-
}
@@ -1051,6 +1048,7 @@ const TestRun = () => {
isSubmitted={isTestSubmitClicked}
isEdit={isTestEditClicked}
setIsRendererReady={setIsRendererReady}
+ commonIssueContent={commonIssueContent}
/>
{isRendererReady && (
@@ -1143,6 +1141,8 @@ const TestRun = () => {
testPlanTitle={
testPlanVersion.title || testPlanVersion.testPlan?.directory || ''
}
+ testPlanVersionString={testPlanVersion.versionString}
+ testPlanVersionReviewLink={`/test-review/${testPlanVersion.id}`}
at={`${testPlanReport.at?.name}${
isViewingRun ? ` ${currentAtVersion?.name}` : ''
}`}
diff --git a/client/components/common/AssignTesterDropdown/index.jsx b/client/components/common/AssignTesterDropdown/index.jsx
index 5d4cd9ceb..f3e1d3092 100644
--- a/client/components/common/AssignTesterDropdown/index.jsx
+++ b/client/components/common/AssignTesterDropdown/index.jsx
@@ -28,6 +28,7 @@ const AssignTesterDropdown = ({
possibleTesters,
onChange,
label,
+ disabled = false,
dropdownAssignTesterButtonRef,
setAlertMessage = () => {}
}) => {
@@ -139,6 +140,7 @@ const AssignTesterDropdown = ({
aria-label="Assign testers"
className="assign-tester"
variant="secondary"
+ disabled={disabled}
>
{renderLabel()}
@@ -216,7 +218,8 @@ AssignTesterDropdown.propTypes = {
label: PropTypes.string,
draftTestPlanRuns: PropTypes.arrayOf(TestPlanRunPropType),
setAlertMessage: PropTypes.func,
- dropdownAssignTesterButtonRef: PropTypes.object
+ dropdownAssignTesterButtonRef: PropTypes.object,
+ disabled: PropTypes.bool
};
export default AssignTesterDropdown;
diff --git a/client/tests/e2e/TestRun.e2e.test.js b/client/tests/e2e/TestRun.e2e.test.js
index 5386ceb8f..48ccdc997 100644
--- a/client/tests/e2e/TestRun.e2e.test.js
+++ b/client/tests/e2e/TestRun.e2e.test.js
@@ -336,7 +336,7 @@ describe('Test Run when signed in as tester', () => {
await page.waitForSelector('h1 ::-p-text(Test 1)');
await page.waitForSelector('button ::-p-text(Next Test)');
- const radioSelector = 'input[type="radio"]';
+ const radioSelector = 'input[type="radio"][id^="pass-"]';
const test1NavSelector = 'nav#test-navigator-nav ol li:nth-child(1)';
const test2NavSelector = 'nav#test-navigator-nav ol li:nth-child(2)';
const nextTestButtonSelector = 'button ::-p-text(Next Test)';
@@ -344,7 +344,7 @@ describe('Test Run when signed in as tester', () => {
// Randomly select radio buttons on first test
const generatedCheckedTest1Count =
- await getGeneratedCheckedAssertionCount(page, radioSelector);
+ await getGeneratedCheckedAssertionCount(page);
// Navigate to test 2 with navigation menu
await page.$eval(test2NavSelector, el => el.querySelector('a').click());
@@ -352,7 +352,7 @@ describe('Test Run when signed in as tester', () => {
await page.waitForSelector('h1 ::-p-text(Test 2:)');
await page.waitForSelector('button ::-p-text(Next Test)');
const generatedCheckedTest2Count =
- await getGeneratedCheckedAssertionCount(page, radioSelector);
+ await getGeneratedCheckedAssertionCount(page);
// Navigate to test 3 with next button
await page.click(nextTestButtonSelector);
diff --git a/client/tests/e2e/snapshots/saved/_candidate-test-plan_24_1.html b/client/tests/e2e/snapshots/saved/_candidate-test-plan_24_1.html
index 2f76d8bff..81b0004b7 100644
--- a/client/tests/e2e/snapshots/saved/_candidate-test-plan_24_1.html
+++ b/client/tests/e2e/snapshots/saved/_candidate-test-plan_24_1.html
@@ -739,7 +739,7 @@
Test Review Options
Test Review Options
class="test-info-entity apg-example-name"
data-testid="apg-example-name">
Test Options
Raise An IssueRaise an Issue
diff --git a/client/tests/e2e/snapshots/saved/_test-plan-report_1.html b/client/tests/e2e/snapshots/saved/_test-plan-report_1.html
index 4a53e4083..08abb623b 100644
--- a/client/tests/e2e/snapshots/saved/_test-plan-report_1.html
+++ b/client/tests/e2e/snapshots/saved/_test-plan-report_1.html
@@ -271,7 +271,9 @@
class="test-info-entity apg-example-name"
data-testid="apg-example-name">
After 'Down Arrow'
+ Raise an issue for 'Down Arrow'
After 'B'
+ Raise an issue for 'B'
After 'F'
+ Raise an issue for 'F'
After 'Tab'
+ Raise an issue for 'Tab'