Skip to content

Commit

Permalink
feat: Only allow exact AT versions for recommended test plan versions (
Browse files Browse the repository at this point in the history
…#1087)

* Add minimum or exact at version to reports

* Quick tweak

* Revert home copy change

* Remove unused field from createTestPlanReport

* Fix undefined var

* Prevent API from creating duplicate reports

* Support primary test plan to be selected

* Fix test

* Add dialog when marking report as final for an admin to select from probably primary test run options

* prioritised -> prioritized typo (british -> american english)

* Avoid displaying primary test plan run confirmation when just 1 run option

* Add atVersion frontend

* Make sure automation dialog always shows when valid

* Make sure existing reports have a minimum at version

* Formatting

* feat: Add resolver for tracking first required AT Version (#1051) Address #792

* Add resolver for finding firstRequiredAtVersion for a RECOMMENDED TestPlanVersion, given an atId

* Update tests

* Fix graphql call when including "firstRequiredAtVersion" under "testPlanVersions"

* Update description of firstRequiredAtVersion

* Rename resolver

* Update status dialog for minimum exact AT versions

* Recommended phase uses exact AT version

* Remove unneeded ternary

* Fix issue caused by using draft reports

* Reduce number of at versions shown in status dialog

* Fix unused var

* Fix incorrect required browser

* Address PR feedback

---------

Co-authored-by: Howard Edwards <howarde.edwards@gmail.com>
  • Loading branch information
alflennik and howard-e committed May 8, 2024
1 parent 19900a4 commit 6a98bc7
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 71 deletions.
5 changes: 4 additions & 1 deletion client/components/GraphQLProvider/GraphQLProvider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const client = new ApolloClient({
typePolicies: {
Query: {
fields: {
testPlanVersion: { merge: true },
testPlanVersions: { merge: false },
testPlanReport: { merge: true },
testPlanReports: { merge: false },
collectionJobByTestPlanRunId: {
Expand All @@ -43,7 +45,8 @@ const client = new ApolloClient({
Mutation: {
fields: {
testPlanReport: { merge: false },
testPlanRun: { merge: false }
testPlanRun: { merge: false },
testPlanVersion: { merge: false }
}
}
}
Expand Down
56 changes: 48 additions & 8 deletions client/components/ManageTestQueue/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,16 @@ const ManageTestQueue = ({

const [selectedAtId, setSelectedAtId] = useState('');
const [selectedBrowserId, setSelectedBrowserId] = useState('');
const [selectAtVersionExactOrMinimum, setSelectedAtVersionExactOrMinimum] =
useState('Exact Version');
const [
selectedAtVersionExactOrMinimum,
setSelectedAtVersionExactOrMinimum
] = useState('Exact Version');
const [selectedReportAtVersionId, setSelectedReportAtVersionId] =
useState(null);
const [
showMinimumAtVersionErrorMessage,
setShowMinimumAtVersionErrorMessage
] = useState(false);

const [addAtVersion] = useMutation(ADD_AT_VERSION_MUTATION);
const [editAtVersion] = useMutation(EDIT_AT_VERSION_MUTATION);
Expand Down Expand Up @@ -337,6 +343,7 @@ const ManageTestQueue = ({

const onAtChange = e => {
const { value } = e.target;
setShowMinimumAtVersionErrorMessage(false);
setSelectedAtId(value);
setSelectedReportAtVersionId(null);
};
Expand All @@ -353,6 +360,8 @@ const ManageTestQueue = ({

const onTestPlanVersionChange = e => {
const { value } = e.target;
setShowMinimumAtVersionErrorMessage(false);
setSelectedAtVersionExactOrMinimum('Exact Version');
setSelectedTestPlanVersionId(value);
};

Expand Down Expand Up @@ -507,6 +516,10 @@ const ManageTestQueue = ({
.find(item => item.id === selectedAtId)
?.atVersions.find(item => item.id === selectedReportAtVersionId);

const selectedTestPlanVersion = allTestPlanVersions.find(
({ id }) => id === selectedTestPlanVersionId
);

return (
<LoadingStatus message={loadingMessage}>
<DisclosureComponent
Expand Down Expand Up @@ -603,6 +616,12 @@ const ManageTestQueue = ({
<Form.Select
onChange={e => {
const { value } = e.target;
setShowMinimumAtVersionErrorMessage(
false
);
setSelectedAtVersionExactOrMinimum(
'Exact Version'
);
updateMatchingTestPlanVersions(
value,
allTestPlanVersions
Expand Down Expand Up @@ -691,13 +710,25 @@ const ManageTestQueue = ({
'Minimum Version'
]}
selectedLabel={
selectAtVersionExactOrMinimum
selectedAtVersionExactOrMinimum
}
onSelect={exactOrMinimum =>
onSelect={exactOrMinimum => {
if (
selectedTestPlanVersion?.phase ===
'RECOMMENDED' &&
exactOrMinimum ===
'Minimum Version'
) {
setShowMinimumAtVersionErrorMessage(
true
);
return;
}

setSelectedAtVersionExactOrMinimum(
exactOrMinimum
)
}
);
}}
/>
<Form.Select
value={selectedReportAtVersionId ?? ''}
Expand All @@ -718,6 +749,15 @@ const ManageTestQueue = ({
</option>
))}
</Form.Select>
{showMinimumAtVersionErrorMessage &&
selectedTestPlanVersion?.phase ===
'RECOMMENDED' ? (
<div role="alert">
The selected test plan version is in
the recommended phase and only exact
versions can be chosen.
</div>
) : null}
</div>
</Form.Group>
<Form.Group className="form-group">
Expand Down Expand Up @@ -751,13 +791,13 @@ const ManageTestQueue = ({
)}
at={ats.find(item => item.id === selectedAtId)}
exactAtVersion={
selectAtVersionExactOrMinimum ===
selectedAtVersionExactOrMinimum ===
'Exact Version'
? exactOrMinimumAtVersion
: null
}
minimumAtVersion={
selectAtVersionExactOrMinimum ===
selectedAtVersionExactOrMinimum ===
'Minimum Version'
? exactOrMinimumAtVersion
: null
Expand Down
7 changes: 4 additions & 3 deletions client/components/TestPlanReportStatusDialog/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,10 @@ const TestPlanReportStatusDialog = ({

if (isRequired) requiredReports += 1;

const key = `${at.name}-${browser.name}-${
testPlanReport?.id ?? 'missing'
}`;
const key =
`${at.name}-${browser.name}-` +
`${minimumAtVersion?.id ?? exactAtVersion?.id}-` +
`${testPlanReport?.id ?? 'missing'}`;

const atVersionFormatted = minimumAtVersion
? `${minimumAtVersion.name} or later`
Expand Down
12 changes: 10 additions & 2 deletions server/graphql-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -1008,11 +1008,19 @@ const graphqlSchema = gql`
"""
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.
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.
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
"""
Expand Down
11 changes: 10 additions & 1 deletion server/models/services/TestPlanReportService.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,14 @@ const createTestPlanReport = async ({
*/
const updateTestPlanReportById = async ({
id,
values: { metrics, testPlanVersionId, vendorReviewStatus, markedFinalAt },
values: {
metrics,
testPlanVersionId,
vendorReviewStatus,
minimumAtVersionId,
exactAtVersionId,
markedFinalAt
},
testPlanReportAttributes = TEST_PLAN_REPORT_ATTRIBUTES,
testPlanRunAttributes = TEST_PLAN_RUN_ATTRIBUTES,
testPlanVersionAttributes = TEST_PLAN_VERSION_ATTRIBUTES,
Expand All @@ -296,6 +303,8 @@ const updateTestPlanReportById = async ({
metrics,
testPlanVersionId,
vendorReviewStatus,
minimumAtVersionId,
exactAtVersionId,
markedFinalAt
},
transaction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ const earliestAtVersionResolver = async (

let earliestAtVersion = null;
for (const testPlanReport of reports.filter(
testPlanReport => testPlanReport.atId == atId
testPlanReport =>
testPlanReport.atId == atId && testPlanReport.markedFinalAt
)) {
const browserId = testPlanReport.browserId;

Expand Down
2 changes: 1 addition & 1 deletion server/resolvers/TestPlanVersion/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const TestPlanVersion = {
testPlan,
gitMessage,
tests,
testPlanReports,
recommendedPhaseTargetDate,
testPlanReports,
testPlanReportStatuses,
earliestAtVersion
};
Expand Down
82 changes: 47 additions & 35 deletions server/resolvers/TestPlanVersion/testPlanReportStatusesResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const testPlanReportStatusesResolver = async (testPlanVersion, _, context) => {

const unsortedStatuses = [];

ats.forEach(at => {
at.browsers.forEach(browser => {
for (const at of ats) {
for (const browser of at.browsers) {
let isRequiredAtBrowser = false;
if (phase === 'DRAFT' || phase === 'CANDIDATE') {
isRequiredAtBrowser = at.candidateBrowsers.some(
Expand All @@ -34,50 +34,59 @@ const testPlanReportStatusesResolver = async (testPlanVersion, _, context) => {
);
}

const atVersions = at.atVersions.sort((a, b) => {
return new Date(a.releasedAt) - new Date(b.releasedAt);
});

const hasNoReports =
Object.keys(indexedTestPlanReports?.[at.id]?.[browser.id] ?? {})
.length === 0;

if (hasNoReports) {
const earliestAtVersion = at.atVersions[0];
if (phase !== 'RECOMMENDED') {
const earliestAtVersion = at.atVersions[0];

unsortedStatuses.push({
isRequired: isRequiredAtBrowser,
at,
browser,
minimumAtVersion: earliestAtVersion,
exactAtVersion: null,
testPlanReport: null
});

continue;
}

const latestAtVersion = atVersions[atVersions.length - 1];

unsortedStatuses.push({
isRequired: isRequiredAtBrowser,
at,
browser,
minimumAtVersion: earliestAtVersion,
exactAtVersion: null,
exactAtVersion: latestAtVersion,
testPlanReport: null
});

return;
continue;
}

let isFirstAtBrowserInstance = true;
let firstTestPlanReportFound = false;
let minimumAtVersionFound = false;

const atVersions = at.atVersions.sort((a, b) => {
return new Date(a.releasedAt) - new Date(b.releasedAt);
});

atVersions.forEach(atVersion => {
const testPlanReports =
indexedTestPlanReports?.[at.id]?.[browser.id]?.[
atVersion.id
] ?? [];

if (testPlanReports.length) {
firstTestPlanReportFound = true;
Object.values(indexedTestPlanReports[at.id][browser.id]).forEach(
testPlanReports => {
testPlanReports.forEach(testPlanReport => {
let isRequired = false;
if (isRequiredAtBrowser && isFirstAtBrowserInstance) {
isFirstAtBrowserInstance = false;
isRequired = true;
}

if (testPlanReport.minimumAtVersion) {
minimumAtVersionFound = true;
}

unsortedStatuses.push({
isRequired,
at,
Expand All @@ -88,30 +97,33 @@ const testPlanReportStatusesResolver = async (testPlanVersion, _, context) => {
populateTestPlanVersion(testPlanReport)
});
});

return;
}
);

if (firstTestPlanReportFound && !minimumAtVersionFound) {
unsortedStatuses.push({
isRequired: false,
at,
browser,
minimumAtVersion: null,
exactAtVersion: atVersion,
testPlanReport: null
});
}
});
});
});
const latestAtVersion = atVersions[atVersions.length - 1];

if (
!minimumAtVersionFound &&
!indexedTestPlanReports[at.id][browser.id][latestAtVersion.id]
) {
unsortedStatuses.push({
isRequired: false,
at,
browser,
minimumAtVersion: null,
exactAtVersion: latestAtVersion,
testPlanReport: null
});
}
}
}

const statuses = unsortedStatuses.sort((a, b) => {
if (a.at.name !== b.at.name) return a.at.name.localeCompare(b.at.name);
if (a.isRequired !== b.isRequired) return a.isRequired ? -1 : 1;
if (a.browser.name !== b.browser.name) {
return a.browser.name.localeCompare(b.browser.name);
}
if (a.isRequired !== b.isRequired) return a.isRequired ? -1 : 1;
const dateA = (a.minimumAtVersion ?? a.exactAtVersion).releasedAt;
const dateB = (b.minimumAtVersion ?? b.exactAtVersion).releasedAt;
return new Date(dateA) - new Date(dateB);
Expand Down
Loading

0 comments on commit 6a98bc7

Please sign in to comment.