Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion schemas/report/v4.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@
"details": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "object",
"unevaluatedProperties": false,
Expand Down
24 changes: 24 additions & 0 deletions src/helpers/report.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,26 @@ const getReportVersion = (report) => {
}
};

const validateReportV4Details = (report, dataVar) => {
const detailIndices = new Map();

for (const [index, detail] of report.details.entries()) {
const key = detail.testId === undefined ?
`detail:${JSON.stringify(detail)}` :
`testId:${detail.testId}`;
const existingIndex = detailIndices.get(key);

if (existingIndex !== undefined) {
throw new Error(
`[${dataVar}].details: must NOT have duplicate items ` +
`(items ## ${existingIndex} and ${index} are identical)`
);
}

detailIndices.set(key, index);
}
};

const validateReport = (report, dataVar = 'report') => {
const reportVersion = getReportVersion(report);
let errors;
Expand Down Expand Up @@ -59,6 +79,10 @@ const validateReport = (report, dataVar = 'report') => {
errors = validateReportV4Ajv.errors;
}

if (errors === undefined || errors.length === 0) {
validateReportV4Details(report, dataVar);
}

break;
default:
throw new Error(`Unknown report version '${reportVersion}'`);
Expand Down
49 changes: 49 additions & 0 deletions test/unit/report.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,14 @@ const testReportOldV3ConfigOnly = {
...testReportOldV3Full,
details: testDetailsOldV3ConfigOnly
};
const testReportV4Full = {
...testReportLatestFull,
version: 4,
details: testDetailsLatest.map((detail, index) => ({
...detail,
testId: `test-id-${index}`
}))
};

describe('report', () => {
afterEach(() => mock.reset());
Expand Down Expand Up @@ -840,6 +848,47 @@ describe('report', () => {
});
});

describe('v4', () => {
it('allows otherwise identical details with distinct test IDs', () => {
const detail = testDetailsLatest[0];
const reportData = {
...testReportV4Full,
details: [{ ...detail, testId: 'test-id-1' }, { ...detail, testId: 'test-id-2' }]
};

mock.method(fs, 'readFileSync', () => JSON.stringify(reportData));

expect(() => new Report(testReportPath)).to.not.throw();
});

it('rejects duplicate test IDs', () => {
const detail = testDetailsLatest[0];
const reportData = {
...testReportV4Full,
details: [
{ ...detail, testId: 'duplicate-test-id' },
{ ...detail, duration: { final: 238, total: 550 }, testId: 'duplicate-test-id' }
]
};

mock.method(fs, 'readFileSync', () => JSON.stringify(reportData));

expect(() => new Report(testReportPath)).to.throw(/must NOT have duplicate items/);
});

it('rejects duplicate details without test IDs', () => {
const detail = testDetailsLatest[0];
const reportData = {
...testReportV4Full,
details: [detail, detail]
};

mock.method(fs, 'readFileSync', () => JSON.stringify(reportData));

expect(() => new Report(testReportPath)).to.throw(/must NOT have duplicate items/);
});
});

describe(`legacy (old v3, cleans to v${latestReportVersion})`, () => {
it('cleans config to configuration', () => {
mock.method(fs, 'readFileSync', () => JSON.stringify(testReportOldV3ConfigOnly));
Expand Down