Skip to content

Commit

Permalink
feat(tests): Include required Test Interface Actions in generated tes…
Browse files Browse the repository at this point in the history
…t docs (#655)

What this does:

- Introduce a new FeatureHelper.describeFeature(..) param to annotate which TI Actions are used in a test
- FeatureHelper.describeFeature(..) will now, via DescribeFeatureRecord, check, after a test run completes, that the TI Actions used are the same as annotated
a. Test report generation only renders this check if it's the only thing that fails. And it will include a special message to ensure that this issue gets looked at by maintainers. The rationale for all this can be seen in code comments
- The TI Actions are included in the auto-generated documentation for each test, and the summary documentation across all tests
  • Loading branch information
lukehesluke authored Mar 18, 2024
1 parent a2e4ca4 commit 9423327
Show file tree
Hide file tree
Showing 152 changed files with 1,113 additions and 590 deletions.
2 changes: 0 additions & 2 deletions packages/openactive-integration-tests/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ test/certification/certification-writer.js
test/global-setup.js
test/helpers/flow-helper.js
test/helpers/index.js
test/helpers/logger.js
test/helpers/mapping.js
test/report-generator.js
test/reporter.js
test/setup.js
test/shared-behaviours/common.js
Expand Down
61 changes: 48 additions & 13 deletions packages/openactive-integration-tests/documentation/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ const INDEX_TESTS_IMPLEMENTED_JSON_FILE = path.join(FEATURES_ROOT, 'tests-implem
* @typedef {FeatureJson & {
* criteriaRequirement?: Map<string, number>,
* sellerCriteriaRequirements?: Map<string, Map<string, number>>,
* testInterfaceActionImplementationRequirements?: Set<string>,
* }} FeatureMetadataItem
* - `criteriaRequirement`: `Map { [opportunityCriteria] => [numOpportunitiesRequired] }`
* e.g. `Map { 'TestOpportunityBookable' => 1 }`
* - `sellerCriteriaRequirements`: `Map { [sellerCriteria] => { [opportunityCriteria] => [numOpportunitiesRequired] } }`
* - `testInterfaceActionImplementationRequirements`: Test Interface Actions
* that need to be implemented for this feature to be tested.
*/

const FEATURES_NOT_REQUIRED_FOR_DEFAULT_JSON = new Set([
Expand Down Expand Up @@ -117,6 +123,12 @@ for (const featureMetadataItem of featureMetadata) {
const sellerCriteriaRequirements = SellerCriteriaRequirements.combine(testMetadataThatAreWithinFeature.map(t => t.sellerCriteriaRequirements));
featureMetadataItem.criteriaRequirement = criteriaRequirement;
featureMetadataItem.sellerCriteriaRequirements = sellerCriteriaRequirements;
featureMetadataItem.testInterfaceActionImplementationRequirements = new Set();
for (const t of testMetadataThatAreWithinFeature) {
for (const action of (t.testInterfaceActions ?? [])) {
featureMetadataItem.testInterfaceActionImplementationRequirements.add(action);
}
}
}

// Save implemented/not-implemented information to a machine-readable (JSON) file.
Expand Down Expand Up @@ -177,24 +189,24 @@ Stub tests are provided in many cases, and test coverage should not be regarded
The tests for these features cover all known edge cases, including both happy and unhappy paths.
| Category | Feature | Specification | Description | Prerequisites per Opportunity Type |
|----------|---------|---------------|-------------|-------------------|
| Category | Feature | Specification | Description | Prerequisites per Opportunity Type | Required Test Interface Actions |
|----------|---------|---------------|-------------|-------------------|-------------------|
${features.filter(f => f.coverageStatus === 'complete').map(f => renderFeatureIndexFeatureFragment(f)).join('')}
${!features.some(f => f.coverageStatus === 'partial') ? '' : `
## Partial Test Coverage
The tests for these features provide partial coverage but do not include all known edgecases, and do not exercise all code paths and error conditions.
| Category | Feature | Specification | Description | Prerequisites per Opportunity Type |
|----------|---------|---------------|-------------|-------------------|
| Category | Feature | Specification | Description | Prerequisites per Opportunity Type | Required Test Interface Actions |
|----------|---------|---------------|-------------|-------------------|-------------------|
${features.filter(f => f.coverageStatus === 'partial').map(f => renderFeatureIndexFeatureFragment(f)).join('')}`}
## No Test Coverage
The tests for these features are fully stubbed, and are not yet implemented.
| Category | Feature | Specification | Description | Prerequisites per Opportunity Type |
|----------|---------|---------------|-------------|-------------------|
| Category | Feature | Specification | Description | Prerequisites per Opportunity Type | Required Test Interface Actions |
|----------|---------|---------------|-------------|-------------------|-------------------|
${features.filter(f => f.coverageStatus === 'none').map(f => renderFeatureIndexFeatureFragment(f)).join('')}
`;
Expand All @@ -204,7 +216,7 @@ ${features.filter(f => f.coverageStatus === 'none').map(f => renderFeatureIndexF
* @param {FeatureMetadataItem} f
*/
function renderFeatureIndexFeatureFragment(f) {
return `| ${f.category} | ${f.name} ([${f.identifier}](./${f.category}/${f.identifier}/README.md)) | ${f.required ? 'Required' : 'Optional'}<br>[View Spec](${f.specificationReference}) | ${f.description} | ${renderCriteriaRequired(f.criteriaRequirement, '')} |
return `| ${f.category} | ${f.name} ([${f.identifier}](./${f.category}/${f.identifier}/README.md)) | ${f.required ? 'Required' : 'Optional'}<br>[View Spec](${f.specificationReference}) | ${f.description} | ${renderCriteriaRequired(f.criteriaRequirement, '')} | ${renderTestInterfaceActionImplementationRequirements([...f.testInterfaceActionImplementationRequirements])} |
`;
}

Expand Down Expand Up @@ -237,9 +249,14 @@ ${f.explainer ? `\n${f.explainer}` : ''}${f.requiredCondition ? `\n${f.requiredC
${f.specificationReference}
Coverage Status: **${f.coverageStatus}**${f.links ? `\n\nSee also: ${f.links.map(l => `[${l.name}](${l.href})`).join(', ')}` : ''}
${renderCriteriaRequired(f.criteriaRequirement, `### Test prerequisites
${renderCriteriaRequired(f.criteriaRequirement, `### Test prerequisites - Opportunities
Opportunities that match the following criteria must exist in the booking system (for each configured \`bookableOpportunityTypesInScope\`) for the configured primary Seller in order to use \`useRandomOpportunities: true\`. Alternatively the following \`testOpportunityCriteria\` values must be supported by the [test interface](https://openactive.io/test-interface/) of the booking system for \`useRandomOpportunities: false\`.
`)}
${renderTestInterfaceActionImplementationRequirements([...f.testInterfaceActionImplementationRequirements], `### Test prerequisites - Test Interface Actions
The following Test Interface Actions must be implemented by the [test interface](https://openactive.io/test-interface/) of the booking system in order to test this feature:
`)}
${f.coverageStatus !== 'none' ? `
Expand All @@ -265,8 +282,8 @@ ${'```'}json
}
${'```'}
| Identifier | Name | Description | Prerequisites per Opportunity Type |
|------------|------|-------------|---------------|
| Identifier | Name | Description | Prerequisites per Opportunity Type | Required Test Interface Actions |
|------------|------|-------------|---------------|-------------------|
${implementedTests.map(t => renderFeatureTest(t)).join('')}` : ''}
${notImplementedTests.length > 0 ? `
Expand All @@ -283,16 +300,16 @@ ${'```'}json
}
${'```'}
` : ''}
| Identifier | Name | Description | Prerequisites per Opportunity Type |
|------------|------|-------------|---------------|
| Identifier | Name | Description | Prerequisites per Opportunity Type | Required Test Interface Actions |
|------------|------|-------------|---------------|-------------------|
${notImplementedTests.map(t => renderFeatureTest(t)).join('')}` : ''}`;
}

/**
* @param {TestModuleExports} t
*/
function renderFeatureTest(t) {
return `| [${t.testIdentifier}](./${renderFeatureTestPath(t)}) | ${t.testName} | ${t.testDescription} | ${renderCriteriaRequired(t.criteriaRequirement, '')} |
return `| [${t.testIdentifier}](./${renderFeatureTestPath(t)}) | ${t.testName} | ${t.testDescription} | ${renderCriteriaRequired(t.criteriaRequirement, '')} | ${renderTestInterfaceActionImplementationRequirements(t.testInterfaceActions ?? [])} |
`;
}

Expand Down Expand Up @@ -324,6 +341,24 @@ function renderCriteriaRequired(criteriaRequired, prefixOverride) {
)).join(', ')}`;
}

/**
* @param {string[] | undefined} testInterfaceActions e.g.
* `['test:AccessChannelUpdateSimulateAction']`
* @param {string} [prefix] If provided, this prefix will be rendered before the
* test interface actions, if there are any.
*/
function renderTestInterfaceActionImplementationRequirements(testInterfaceActions, prefix = '') {
if (!testInterfaceActions || testInterfaceActions.length === 0) {
return '';
}
const sorted = [...testInterfaceActions].sort();
const result = sorted.map((r) => {
const nonPrefixed = r.replace(/^test:/, '');
return `[${nonPrefixed}](https://openactive.io/test-interface#${nonPrefixed})`;
}).join(', ');
return `${prefix}${result}`;
}

// # JSON rendering functions

/**
Expand Down
Loading

0 comments on commit 9423327

Please sign in to comment.