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 && ( +
+ + Raise an Issue + +
+ )} ); }; 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" >
- Test Plan: {testPlanTitle} + Test Plan:  + {testPlanName}
@@ -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">
    - Test Plan: Toggle Button + Test Plan: Toggle Button V22.05.18

  • 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'