@jahia/cypress 8.1.1 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @jahia/cypress Changelog
2
2
 
3
+ ## 8.2.0
4
+
5
+ * Add `context.tag()` function for adding tags (user-defined labels) that can be attached to test suites and individual tests to provide metadata about test characteristics, scope, and purpose (#221)
6
+
3
7
  ## 8.1.1
4
8
 
5
9
  * Use default Jahia ref. while fetching version; fail safe when version can't be fetched.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Tag a test suite or individual test.
3
+ *
4
+ * Tags are user-defined labels that provide metadata about tests. They are collected during
5
+ * test execution and synchronized to TestRail test cases by jahia-reporter, enabling filtering,
6
+ * categorization, and custom dashboard reporting.
7
+ *
8
+ * - Call inside `describe()` to tag every test in the suite (tags are inherited by all nested tests).
9
+ * - Call inside `it()` to tag only that specific test.
10
+ *
11
+ * Tags are:
12
+ * - Collected and stored in the mochawesome report under each test's `context` field
13
+ * - Automatically synced to TestRail by jahia-reporter for dashboard and filtering
14
+ * - Deduplicated (each unique tag appears once per test)
15
+ * - Inherited by nested describe blocks
16
+ *
17
+ * @param {string[]} tags - array of tags to be added
18
+ * @return {void}
19
+ *
20
+ * @example
21
+ * import {context} from '@jahia/cypress';
22
+ * describe('My suite', () => {
23
+ * context.tag('smoke', 'regression', 'p0');
24
+ *
25
+ * it('my test', () => {
26
+ * context.tag('critical');
27
+ * // effective tags: ['smoke', 'regression', 'p0', 'critical']
28
+ * });
29
+ * });
30
+ *
31
+ * @see docs/context-reporter.md for details
32
+ */
33
+ declare function tag(...tags: string[]): void;
34
+ /** Public export */
35
+ export declare const context: {
36
+ tag: typeof tag;
37
+ };
38
+ export {};
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
3
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
4
+ if (ar || !(i in from)) {
5
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
6
+ ar[i] = from[i];
7
+ }
8
+ }
9
+ return to.concat(ar || Array.prototype.slice.call(from));
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.context = void 0;
16
+ exports.collect = collect;
17
+ /* eslint-disable @typescript-eslint/no-explicit-any */
18
+ var addContext_1 = __importDefault(require("mochawesome/addContext"));
19
+ /**
20
+ * Tag a test suite or individual test.
21
+ *
22
+ * Tags are user-defined labels that provide metadata about tests. They are collected during
23
+ * test execution and synchronized to TestRail test cases by jahia-reporter, enabling filtering,
24
+ * categorization, and custom dashboard reporting.
25
+ *
26
+ * - Call inside `describe()` to tag every test in the suite (tags are inherited by all nested tests).
27
+ * - Call inside `it()` to tag only that specific test.
28
+ *
29
+ * Tags are:
30
+ * - Collected and stored in the mochawesome report under each test's `context` field
31
+ * - Automatically synced to TestRail by jahia-reporter for dashboard and filtering
32
+ * - Deduplicated (each unique tag appears once per test)
33
+ * - Inherited by nested describe blocks
34
+ *
35
+ * @param {string[]} tags - array of tags to be added
36
+ * @return {void}
37
+ *
38
+ * @example
39
+ * import {context} from '@jahia/cypress';
40
+ * describe('My suite', () => {
41
+ * context.tag('smoke', 'regression', 'p0');
42
+ *
43
+ * it('my test', () => {
44
+ * context.tag('critical');
45
+ * // effective tags: ['smoke', 'regression', 'p0', 'critical']
46
+ * });
47
+ * });
48
+ *
49
+ * @see docs/context-reporter.md for details
50
+ */
51
+ function tag() {
52
+ var _a;
53
+ var tags = [];
54
+ for (var _i = 0; _i < arguments.length; _i++) {
55
+ tags[_i] = arguments[_i];
56
+ }
57
+ if (Cypress.currentTest) {
58
+ // Inside it() — Cypress is running a test, attach tags directly to the Mocha runnable
59
+ var runnable = cy.state('runnable');
60
+ runnable._tags = __spreadArray(__spreadArray([], ((_a = runnable._tags) !== null && _a !== void 0 ? _a : []), true), tags, true);
61
+ }
62
+ else {
63
+ // Inside describe() — collection phase, schedule a before() hook to tag the suite
64
+ before(function () {
65
+ var _a, _b;
66
+ var suite = (_a = this.currentTest) === null || _a === void 0 ? void 0 : _a.parent;
67
+ if (suite) {
68
+ var taggedSuite = suite;
69
+ taggedSuite._tags = __spreadArray(__spreadArray([], ((_b = taggedSuite._tags) !== null && _b !== void 0 ? _b : []), true), tags, true);
70
+ }
71
+ });
72
+ }
73
+ }
74
+ /**
75
+ * Internal function to collect all tags upon 'test:after:run' event and add them to the mochawesome context.
76
+ *
77
+ * Walks up the suite chain to collect inherited tags and combines them with test-specific tags.
78
+ * The collected tags are added to the test context in the mochawesome report, where jahia-reporter
79
+ * can extract them and sync to the corresponding TestRail test case.
80
+ *
81
+ * @param test
82
+ * @param runnable
83
+ * @internal to be used in registerSupport only
84
+ */
85
+ function collect(test, runnable) {
86
+ var _a, _b;
87
+ var taggedRunnable = runnable;
88
+ // Add video context
89
+ // addContext({test}, {title: 'video', value: `videos/${Cypress.spec.relative.replace('/.cy.*', '').replace('cypress/e2e/', '')}.mp4`});
90
+ // Walk up the suite chain (outermost first) to collect inherited suite tags
91
+ var suiteTags = [];
92
+ var parent = taggedRunnable.parent;
93
+ while (parent) {
94
+ if ((_a = parent._tags) === null || _a === void 0 ? void 0 : _a.length) {
95
+ suiteTags.unshift.apply(suiteTags, parent._tags);
96
+ }
97
+ parent = parent.parent;
98
+ }
99
+ // Collect all unique tags (suite + test) and add to context
100
+ var allTags = Array.from(new Set(__spreadArray(__spreadArray([], suiteTags, true), ((_b = taggedRunnable._tags) !== null && _b !== void 0 ? _b : []), true)));
101
+ if (allTags.length > 0) {
102
+ (0, addContext_1.default)({ test: test }, { title: 'tags', value: allTags });
103
+ }
104
+ // Add screenshot context if test failed
105
+ // if (test.state === 'failed') {
106
+ // addContext({test}, {title: 'screenshot', value: `screenshots/${Cypress.spec.relative.replace('cypress/e2e/', '')}/${runnable.parent.title} -- ${test.title} (failed).png`});
107
+ // }
108
+ }
109
+ /** Public export */
110
+ exports.context = { tag: tag };
@@ -8,3 +8,4 @@ export * from './jsErrorsLogger';
8
8
  export * from './jfaker';
9
9
  export * from './browserHelper';
10
10
  export * from './modSince';
11
+ export * from './contextReporter';
@@ -24,3 +24,4 @@ __exportStar(require("./jsErrorsLogger"), exports);
24
24
  __exportStar(require("./jfaker"), exports);
25
25
  __exportStar(require("./browserHelper"), exports);
26
26
  __exportStar(require("./modSince"), exports);
27
+ __exportStar(require("./contextReporter"), exports);
@@ -21,6 +21,7 @@ var repeatUntil_1 = require("./repeatUntil");
21
21
  var testStep_1 = require("./testStep");
22
22
  var jfaker_1 = require("./jfaker");
23
23
  var modSince_1 = require("./modSince");
24
+ var contextReporter_1 = require("./contextReporter");
24
25
  var registerSupport = function () {
25
26
  Cypress.Commands.add('apolloClient', apollo_1.apolloClient);
26
27
  Cypress.Commands.add('apollo', { prevSubject: 'optional' }, apollo_1.apollo);
@@ -56,5 +57,11 @@ var registerSupport = function () {
56
57
  var newOptions = __assign({ parseSpecialCharSequences: parseSpecialCharSequences }, options);
57
58
  return originalFn(element, text, newOptions);
58
59
  });
60
+ /**
61
+ * Listen to the 'test:after:run' event to collect tags and other context information after each test execution.
62
+ */
63
+ Cypress.on('test:after:run', function (test, runnable) {
64
+ (0, contextReporter_1.collect)(test, runnable);
65
+ });
59
66
  };
60
67
  exports.registerSupport = registerSupport;
@@ -0,0 +1,104 @@
1
+ # Context Reporter: Test Tags and Integration with TestRail
2
+
3
+ ## Overview
4
+
5
+ Test tags are user-defined labels that can be attached to test suites and individual tests to provide metadata about test characteristics, scope, and purpose. Tags are collected during test execution and included in the mochawesome test report.
6
+
7
+ ## Integration with TestRail and jahia-reporter
8
+
9
+ Tags defined in Cypress tests are **automatically synchronized to TestRail test cases** by the `jahia-reporter` tool during the test reporting phase. This enables:
10
+
11
+ - **Test categorization** in TestRail for better organization
12
+ - **Dashboard filtering** based on test characteristics
13
+ - **Reporting dashboards** that slice data by tag combinations (e.g., smoke tests, regression, performance, critical path)
14
+ - **Traceability** linking test runs to business requirements or feature areas
15
+
16
+ ## Usage
17
+
18
+ ### Basic Syntax
19
+
20
+ Use the `tag()` function to attach one or more tags:
21
+
22
+ ```typescript
23
+ import {context} from '@jahia/cypress';
24
+
25
+ describe('Authentication', () => {
26
+ context.tag('smoke', 'critical');
27
+
28
+ it('should login successfully', () => {
29
+ context.tag('p1'); // Add P1 severity
30
+ cy.login();
31
+ cy.url().should('include', '/home');
32
+ });
33
+
34
+ it('should logout successfully', () => {
35
+ cy.logout();
36
+ });
37
+ });
38
+ ```
39
+
40
+ ### Where to Call
41
+
42
+ - **In `describe()`**: Tags apply to **all nested tests** in the suite (inherited by child suites and tests)
43
+ - **In `it()`**: Tags apply to **only that specific test**
44
+ - **Both**: Combine suite-level and test-level tags (both are collected)
45
+
46
+ ### Example: Multi-Level Tagging
47
+
48
+ ```typescript
49
+ import {context} from '@jahia/cypress';
50
+
51
+ describe('Content Management', () => {
52
+ context.tag('regression', 'content'); // Suite-level tags
53
+
54
+ describe('Publishing Workflow', () => {
55
+ context.tag('critical'); // Additional suite-level tag
56
+
57
+ it('should publish page', () => {
58
+ context.tag('p0', 'smoke'); // Test-level tags
59
+ // effective tags: ['regression', 'content', 'critical', 'p0', 'smoke']
60
+ });
61
+
62
+ it('should unpublish page', () => {
63
+ // effective tags: ['regression', 'content', 'critical']
64
+ });
65
+ });
66
+ });
67
+ ```
68
+
69
+ ## Implementation Details
70
+ - Tags are collected during test execution via Mocha hooks and then added to the test's `context`.
71
+ - Suite tags are inherited by nested describe blocks and tests.
72
+ - All unique tags are deduplicated.
73
+
74
+ Tags are stored as an object (`{title: <title>, value: <value>}`) in order to be properly parsed by mocha html-reporter.
75
+ Example:
76
+ ```json
77
+ {title: 'tags', value: ['tag1', 'tag2', 'tag3']}
78
+ ```
79
+
80
+ Finally, the `context` field in Cypress report will contain a stringified JSON array of tags meta-info along with other context information added by the user.
81
+
82
+ Example of `context` field in the report (note - array contains both tags meta-info and user-added context info like video path; array is stringified by `mochawesome` reporter):
83
+ ```json
84
+ "context": "[\n {\n \"title\": \"tags\",\n \"value\": [\n \"graphql-api-upa\",\n \"upa\",\n \"custom-factor\",\n \"P1\",\n \"authentication\"\n ]\n },\n \"videos/graphQL.mfa.customFactor.cy.ts.mp4\"\n]",
85
+ ```
86
+ This `context` field will be parsed by `jahia-reporter` afterward to extract the tags and sync them to `TestRail`. If the `context` field doesn't contain tags in expected format, it will be ignored by `jahia-reporter` and labels in `TestRail` won't be updated.
87
+
88
+
89
+ ## Best Practices
90
+
91
+ 1. **Use consistent tag names** across your test suite
92
+ 2. **Keep tag values simple** (lowercase, no spaces, use hyphens for multi-word tags)
93
+ 3. **Avoid overtagging** — use a reasonable number of tags (3-5 per test)
94
+ 4. **Combine categories** — mix priority, type, and feature tags for flexibility
95
+ 5. **Use suite-level tags** for common characteristics (saves repetition)
96
+ 6. **Add test-level tags** for exceptions or special cases
97
+
98
+ ## Troubleshooting
99
+
100
+ ### Tags not appearing in TestRail
101
+ - Ensure `context.tag()` is called at the right scope (in `describe()` or `it()`)
102
+ - Check that jahia-reporter is configured to sync tags to TestRail
103
+ - Verify the test report is being generated and contains `context` attribute with tags
104
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jahia/cypress",
3
- "version": "8.1.1",
3
+ "version": "8.2.0",
4
4
  "scripts": {
5
5
  "build": "tsc",
6
6
  "lint": "eslint src -c .eslintrc.json --ext .ts --max-warnings=0"
@@ -31,6 +31,7 @@
31
31
  "eslint-plugin-jest": "^29.15.0",
32
32
  "eslint-plugin-react": "^7.32.2",
33
33
  "eslint-plugin-react-hooks": "^4.6.0",
34
+ "mochawesome": "^6.3.1",
34
35
  "typescript": "^5.9.3"
35
36
  },
36
37
  "dependencies": {
@@ -0,0 +1,96 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import addContext from 'mochawesome/addContext';
3
+
4
+ type TaggedRunnable = Mocha.Runnable & {_tags?: string[]};
5
+ type TaggedSuite = Mocha.Suite & {_tags?: string[]};
6
+
7
+ /**
8
+ * Tag a test suite or individual test.
9
+ *
10
+ * Tags are user-defined labels that provide metadata about tests. They are collected during
11
+ * test execution and synchronized to TestRail test cases by jahia-reporter, enabling filtering,
12
+ * categorization, and custom dashboard reporting.
13
+ *
14
+ * - Call inside `describe()` to tag every test in the suite (tags are inherited by all nested tests).
15
+ * - Call inside `it()` to tag only that specific test.
16
+ *
17
+ * Tags are:
18
+ * - Collected and stored in the mochawesome report under each test's `context` field
19
+ * - Automatically synced to TestRail by jahia-reporter for dashboard and filtering
20
+ * - Deduplicated (each unique tag appears once per test)
21
+ * - Inherited by nested describe blocks
22
+ *
23
+ * @param {string[]} tags - array of tags to be added
24
+ * @return {void}
25
+ *
26
+ * @example
27
+ * import {context} from '@jahia/cypress';
28
+ * describe('My suite', () => {
29
+ * context.tag('smoke', 'regression', 'p0');
30
+ *
31
+ * it('my test', () => {
32
+ * context.tag('critical');
33
+ * // effective tags: ['smoke', 'regression', 'p0', 'critical']
34
+ * });
35
+ * });
36
+ *
37
+ * @see docs/context-reporter.md for details
38
+ */
39
+ function tag(...tags: string[]): void {
40
+ if (Cypress.currentTest) {
41
+ // Inside it() — Cypress is running a test, attach tags directly to the Mocha runnable
42
+ const runnable = (cy as any).state('runnable') as TaggedRunnable;
43
+ runnable._tags = [...(runnable._tags ?? []), ...tags];
44
+ } else {
45
+ // Inside describe() — collection phase, schedule a before() hook to tag the suite
46
+ before(function () {
47
+ const suite = this.currentTest?.parent;
48
+ if (suite) {
49
+ const taggedSuite = suite as TaggedSuite;
50
+ taggedSuite._tags = [...(taggedSuite._tags ?? []), ...tags];
51
+ }
52
+ });
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Internal function to collect all tags upon 'test:after:run' event and add them to the mochawesome context.
58
+ *
59
+ * Walks up the suite chain to collect inherited tags and combines them with test-specific tags.
60
+ * The collected tags are added to the test context in the mochawesome report, where jahia-reporter
61
+ * can extract them and sync to the corresponding TestRail test case.
62
+ *
63
+ * @param test
64
+ * @param runnable
65
+ * @internal to be used in registerSupport only
66
+ */
67
+ export function collect(test: any, runnable: any): void {
68
+ const taggedRunnable = runnable as TaggedRunnable;
69
+ // Add video context
70
+ // addContext({test}, {title: 'video', value: `videos/${Cypress.spec.relative.replace('/.cy.*', '').replace('cypress/e2e/', '')}.mp4`});
71
+
72
+ // Walk up the suite chain (outermost first) to collect inherited suite tags
73
+ const suiteTags: string[] = [];
74
+ let parent = taggedRunnable.parent as TaggedSuite | undefined;
75
+ while (parent) {
76
+ if (parent._tags?.length) {
77
+ suiteTags.unshift(...parent._tags);
78
+ }
79
+
80
+ parent = parent.parent as TaggedSuite | undefined;
81
+ }
82
+
83
+ // Collect all unique tags (suite + test) and add to context
84
+ const allTags = Array.from(new Set([...suiteTags, ...(taggedRunnable._tags ?? [])]));
85
+ if (allTags.length > 0) {
86
+ addContext({test}, {title: 'tags', value: allTags});
87
+ }
88
+
89
+ // Add screenshot context if test failed
90
+ // if (test.state === 'failed') {
91
+ // addContext({test}, {title: 'screenshot', value: `screenshots/${Cypress.spec.relative.replace('cypress/e2e/', '')}/${runnable.parent.title} -- ${test.title} (failed).png`});
92
+ // }
93
+ }
94
+
95
+ /** Public export */
96
+ export const context = {tag};
@@ -8,3 +8,4 @@ export * from './jsErrorsLogger';
8
8
  export * from './jfaker';
9
9
  export * from './browserHelper';
10
10
  export * from './modSince';
11
+ export * from './contextReporter';
@@ -7,6 +7,7 @@ import {repeatUntil} from './repeatUntil';
7
7
  import {step} from './testStep';
8
8
  import {jfaker} from './jfaker';
9
9
  import {modSince} from './modSince';
10
+ import {collect as contextCollector} from './contextReporter';
10
11
 
11
12
  export const registerSupport = (): void => {
12
13
  Cypress.Commands.add('apolloClient', apolloClient);
@@ -53,4 +54,11 @@ export const registerSupport = (): void => {
53
54
  return originalFn(element, text, newOptions);
54
55
  }
55
56
  );
57
+
58
+ /**
59
+ * Listen to the 'test:after:run' event to collect tags and other context information after each test execution.
60
+ */
61
+ Cypress.on('test:after:run', (test, runnable) => {
62
+ contextCollector(test, runnable);
63
+ });
56
64
  };
package/tsconfig.json CHANGED
@@ -4,6 +4,7 @@
4
4
  "outDir": "./dist",
5
5
  "module": "commonjs",
6
6
  "declaration": true,
7
+ "stripInternal": true,
7
8
  "moduleResolution": "node",
8
9
  "allowSyntheticDefaultImports": true,
9
10
  "esModuleInterop": true,