@jahia/agentic 0.2.0 → 0.3.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 +4 -0
- package/README.md +28 -0
- package/dist/claude/.claude/skills/jahia/SKILL.md +18 -10
- package/dist/claude/.claude/skills/jahia-content/SKILL.md +102 -84
- package/dist/claude/.claude/skills/jahia-content-create-content/SKILL.md +255 -280
- package/dist/claude/.claude/skills/jahia-content-explore-structure/SKILL.md +187 -96
- package/dist/claude/.claude/skills/jahia-content-media-upload/SKILL.md +197 -0
- package/dist/claude/.claude/skills/jahia-content-move-content/SKILL.md +160 -165
- package/dist/claude/.claude/skills/jahia-content-organize/SKILL.md +209 -0
- package/dist/claude/.claude/skills/jahia-content-publish/SKILL.md +181 -0
- package/dist/claude/.claude/skills/jahia-content-query-content/SKILL.md +122 -92
- package/dist/claude/.claude/skills/jahia-content-translate-content/SKILL.md +154 -225
- package/dist/claude/.claude/skills/jahia-dev-cypress/SKILL.md +150 -330
- package/dist/claude/.claude/skills/jahia-dev-query-content/SKILL.md +93 -296
- package/dist/claude/.claude/skills/jahia-jcr-sql2/SKILL.md +258 -0
- package/dist/claude/CLAUDE.md +1 -7
- package/dist/codex/.agents/skills/jahia/SKILL.md +18 -10
- package/dist/codex/.agents/skills/jahia-content/SKILL.md +102 -84
- package/dist/codex/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
- package/dist/codex/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
- package/dist/codex/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
- package/dist/codex/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
- package/dist/codex/.agents/skills/jahia-content-organize/SKILL.md +209 -0
- package/dist/codex/.agents/skills/jahia-content-publish/SKILL.md +181 -0
- package/dist/codex/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
- package/dist/codex/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
- package/dist/codex/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
- package/dist/codex/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
- package/dist/codex/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
- package/dist/codex/AGENTS.md +2 -4
- package/dist/copilot/.agents/skills/jahia/SKILL.md +18 -10
- package/dist/copilot/.agents/skills/jahia-content/SKILL.md +102 -84
- package/dist/copilot/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
- package/dist/copilot/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
- package/dist/copilot/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
- package/dist/copilot/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
- package/dist/copilot/.agents/skills/jahia-content-organize/SKILL.md +209 -0
- package/dist/copilot/.agents/skills/jahia-content-publish/SKILL.md +181 -0
- package/dist/copilot/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
- package/dist/copilot/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
- package/dist/copilot/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
- package/dist/copilot/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
- package/dist/copilot/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
- package/dist/copilot/AGENTS.md +2 -4
- package/dist/cursor/.agents/skills/jahia/SKILL.md +18 -10
- package/dist/cursor/.agents/skills/jahia-content/SKILL.md +102 -84
- package/dist/cursor/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
- package/dist/cursor/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
- package/dist/cursor/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
- package/dist/cursor/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
- package/dist/cursor/.agents/skills/jahia-content-organize/SKILL.md +209 -0
- package/dist/cursor/.agents/skills/jahia-content-publish/SKILL.md +181 -0
- package/dist/cursor/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
- package/dist/cursor/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
- package/dist/cursor/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
- package/dist/cursor/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
- package/dist/cursor/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
- package/dist/gemini/.agents/skills/jahia/SKILL.md +18 -10
- package/dist/gemini/.agents/skills/jahia-content/SKILL.md +102 -84
- package/dist/gemini/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
- package/dist/gemini/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
- package/dist/gemini/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
- package/dist/gemini/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
- package/dist/gemini/.agents/skills/jahia-content-organize/SKILL.md +209 -0
- package/dist/gemini/.agents/skills/jahia-content-publish/SKILL.md +181 -0
- package/dist/gemini/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
- package/dist/gemini/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
- package/dist/gemini/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
- package/dist/gemini/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
- package/dist/gemini/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
- package/dist/gemini/AGENTS.md +2 -4
- package/dist/gemini/GEMINI.md +2 -2
- package/dist/opencode/.agents/skills/jahia/SKILL.md +18 -10
- package/dist/opencode/.agents/skills/jahia-content/SKILL.md +102 -84
- package/dist/opencode/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
- package/dist/opencode/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
- package/dist/opencode/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
- package/dist/opencode/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
- package/dist/opencode/.agents/skills/jahia-content-organize/SKILL.md +209 -0
- package/dist/opencode/.agents/skills/jahia-content-publish/SKILL.md +181 -0
- package/dist/opencode/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
- package/dist/opencode/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
- package/dist/opencode/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
- package/dist/opencode/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
- package/dist/opencode/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
- package/dist/opencode/AGENTS.md +2 -4
- package/dist/windsurf/.windsurf/skills/jahia/SKILL.md +18 -10
- package/dist/windsurf/.windsurf/skills/jahia-content/SKILL.md +102 -84
- package/dist/windsurf/.windsurf/skills/jahia-content-create-content/SKILL.md +255 -280
- package/dist/windsurf/.windsurf/skills/jahia-content-explore-structure/SKILL.md +187 -96
- package/dist/windsurf/.windsurf/skills/jahia-content-media-upload/SKILL.md +197 -0
- package/dist/windsurf/.windsurf/skills/jahia-content-move-content/SKILL.md +160 -165
- package/dist/windsurf/.windsurf/skills/jahia-content-organize/SKILL.md +209 -0
- package/dist/windsurf/.windsurf/skills/jahia-content-publish/SKILL.md +181 -0
- package/dist/windsurf/.windsurf/skills/jahia-content-query-content/SKILL.md +122 -92
- package/dist/windsurf/.windsurf/skills/jahia-content-translate-content/SKILL.md +154 -225
- package/dist/windsurf/.windsurf/skills/jahia-dev-cypress/SKILL.md +150 -330
- package/dist/windsurf/.windsurf/skills/jahia-dev-query-content/SKILL.md +93 -296
- package/dist/windsurf/.windsurf/skills/jahia-jcr-sql2/SKILL.md +258 -0
- package/dist/windsurf/AGENTS.md +2 -4
- package/package.json +3 -3
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jahia-dev-cypress
|
|
3
|
-
description: Write and scaffold Cypress e2e tests for a Jahia JS template set. Covers
|
|
3
|
+
description: Write and scaffold Cypress e2e tests for a Jahia JS template set. Covers test-package structure, site seeding, isolation, content setup, rendering assertions, and stable selector strategy.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Skill: jahia-dev-cypress
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
A new component, content type, or page template has been built in a JS template set and needs Cypress coverage. Per the CTO guidelines, tests ship alongside the component — not retroactively.
|
|
8
|
+
Use this skill when a Jahia JS template set needs Cypress end-to-end coverage.
|
|
9
|
+
Tests should ship with the component or page template, not weeks later.
|
|
11
10
|
|
|
12
11
|
---
|
|
13
12
|
|
|
14
13
|
## Test directory layout
|
|
15
14
|
|
|
16
|
-
Tests live in a
|
|
15
|
+
Tests live in a separate `tests/` directory at the module root.
|
|
17
16
|
|
|
18
17
|
```
|
|
19
18
|
<module>/
|
|
20
|
-
├── src/
|
|
19
|
+
├── src/
|
|
21
20
|
├── settings/
|
|
22
|
-
├── tests/
|
|
21
|
+
├── tests/
|
|
23
22
|
│ ├── package.json
|
|
24
23
|
│ ├── cypress.config.ts
|
|
25
24
|
│ ├── .env.example
|
|
@@ -32,42 +31,41 @@ Tests live in a **separate `tests/` directory** at the module root — never ins
|
|
|
32
31
|
│ │ ├── fixtures/
|
|
33
32
|
│ │ │ └── graphql/
|
|
34
33
|
│ │ │ └── mutation/
|
|
35
|
-
│ │ │ └── *.graphql
|
|
36
34
|
│ │ ├── plugins/
|
|
37
35
|
│ │ │ └── index.js
|
|
38
36
|
│ │ └── support/
|
|
39
37
|
│ │ ├── commands.ts
|
|
40
|
-
│ │
|
|
38
|
+
│ │ ├── constants.ts
|
|
39
|
+
│ │ └── e2e.ts
|
|
41
40
|
│ └── results/
|
|
42
|
-
│ └── .gitignore
|
|
43
41
|
└── package.json
|
|
44
42
|
```
|
|
45
43
|
|
|
46
44
|
---
|
|
47
45
|
|
|
48
|
-
## Step 1 — Scaffold the
|
|
46
|
+
## Step 1 — Scaffold the Cypress package
|
|
49
47
|
|
|
50
48
|
Create `tests/package.json`:
|
|
51
49
|
|
|
52
50
|
```json
|
|
53
51
|
{
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
52
|
+
"name": "<module-name>-cypress",
|
|
53
|
+
"private": true,
|
|
54
|
+
"scripts": {
|
|
55
|
+
"e2e:ci": "cypress run",
|
|
56
|
+
"e2e:debug": "cypress open"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@jahia/cypress": "^7.1.0",
|
|
60
|
+
"cypress": "^14.0.0",
|
|
61
|
+
"cypress-terminal-report": "^5.3.12",
|
|
62
|
+
"typescript": "^5.0.0"
|
|
63
|
+
},
|
|
64
|
+
"packageManager": "yarn@4.12.0"
|
|
67
65
|
}
|
|
68
66
|
```
|
|
69
67
|
|
|
70
|
-
Install:
|
|
68
|
+
Install with:
|
|
71
69
|
|
|
72
70
|
```bash
|
|
73
71
|
cd tests && yarn install
|
|
@@ -75,371 +73,193 @@ cd tests && yarn install
|
|
|
75
73
|
|
|
76
74
|
---
|
|
77
75
|
|
|
78
|
-
## Step 2 —
|
|
76
|
+
## Step 2 — Configure Cypress
|
|
79
77
|
|
|
80
78
|
```typescript
|
|
81
|
-
|
|
82
|
-
import
|
|
83
|
-
import * as fs from 'node:fs'
|
|
79
|
+
import { defineConfig } from 'cypress';
|
|
80
|
+
import * as fs from 'node:fs';
|
|
84
81
|
|
|
85
82
|
export default defineConfig({
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
},
|
|
109
|
-
})
|
|
110
|
-
return config
|
|
83
|
+
chromeWebSecurity: false,
|
|
84
|
+
defaultCommandTimeout: 10000,
|
|
85
|
+
requestTimeout: 300000,
|
|
86
|
+
responseTimeout: 300000,
|
|
87
|
+
viewportWidth: 1366,
|
|
88
|
+
viewportHeight: 768,
|
|
89
|
+
watchForFileChanges: false,
|
|
90
|
+
screenshotsFolder: './results/screenshots',
|
|
91
|
+
videosFolder: './results/videos',
|
|
92
|
+
e2e: {
|
|
93
|
+
baseUrl: 'http://localhost:8080',
|
|
94
|
+
specPattern: ['**/**.cy.ts'],
|
|
95
|
+
setupNodeEvents(on, config) {
|
|
96
|
+
require('@jahia/cypress/dist/plugins/registerPlugins').registerPlugins(on, config);
|
|
97
|
+
require('cypress-terminal-report/src/installLogsPrinter')(on, {
|
|
98
|
+
printLogsToConsole: 'onFail',
|
|
99
|
+
outputRoot: config.projectRoot + '/results/',
|
|
100
|
+
});
|
|
101
|
+
on('task', {
|
|
102
|
+
readFileMaybe(filename) {
|
|
103
|
+
if (fs.existsSync(filename)) return fs.readFileSync(filename, 'utf8');
|
|
104
|
+
return null;
|
|
111
105
|
},
|
|
106
|
+
});
|
|
107
|
+
return config;
|
|
112
108
|
},
|
|
113
|
-
}
|
|
109
|
+
},
|
|
110
|
+
});
|
|
114
111
|
```
|
|
115
112
|
|
|
116
113
|
---
|
|
117
114
|
|
|
118
|
-
## Step 3 —
|
|
115
|
+
## Step 3 — Keep tests isolated
|
|
116
|
+
|
|
117
|
+
### Site-per-suite isolation
|
|
118
|
+
|
|
119
|
+
Every suite should create its own site in `before()` and delete it in `after()`.
|
|
120
|
+
Do not rely on pre-existing sites.
|
|
119
121
|
|
|
120
122
|
```typescript
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
import { createSite, deleteSite } from '@jahia/cypress';
|
|
124
|
+
|
|
125
|
+
describe('Hero banner', () => {
|
|
126
|
+
const SITE_KEY = 'hero-banner-tests';
|
|
127
|
+
|
|
128
|
+
before(() => {
|
|
129
|
+
cy.login();
|
|
130
|
+
createSite(SITE_KEY, {
|
|
131
|
+
templateSet: 'my-module',
|
|
132
|
+
locale: 'en',
|
|
133
|
+
languages: 'en,fr',
|
|
134
|
+
serverName: 'localhost',
|
|
135
|
+
});
|
|
136
|
+
cy.logout();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
after(() => {
|
|
140
|
+
cy.login();
|
|
141
|
+
deleteSite(SITE_KEY);
|
|
142
|
+
cy.logout();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
124
145
|
```
|
|
125
146
|
|
|
147
|
+
### Session hygiene
|
|
148
|
+
|
|
149
|
+
Use `beforeEach` / `afterEach` for login boundaries when the scenario needs authentication.
|
|
150
|
+
|
|
126
151
|
---
|
|
127
152
|
|
|
128
|
-
## Step 4 —
|
|
153
|
+
## Step 4 — Keep the three mandatory spec files
|
|
129
154
|
|
|
130
|
-
Every component
|
|
155
|
+
Every component test folder should contain:
|
|
131
156
|
|
|
132
|
-
|
|
157
|
+
- `happy-path.cy.ts`
|
|
158
|
+
- `authorization.cy.ts`
|
|
159
|
+
- `edge-cases.cy.ts`
|
|
133
160
|
|
|
134
|
-
|
|
135
|
-
import { addNode, createSite, deleteSite, publishAndWaitJobEnding } from '@jahia/cypress'
|
|
136
|
-
import { SITE_KEY, TEMPLATE_SET } from '../../support/constants'
|
|
137
|
-
|
|
138
|
-
describe('<ComponentName> — happy path', () => {
|
|
139
|
-
before(() => {
|
|
140
|
-
cy.login()
|
|
141
|
-
createSite(SITE_KEY, {
|
|
142
|
-
templateSet: TEMPLATE_SET,
|
|
143
|
-
locale: 'en',
|
|
144
|
-
languages: 'en,fr',
|
|
145
|
-
serverName: 'localhost',
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
// Seed a page with a test component
|
|
149
|
-
addNode({
|
|
150
|
-
parentPathOrId: `/sites/${SITE_KEY}/home`,
|
|
151
|
-
name: 'testPage',
|
|
152
|
-
primaryNodeType: 'jnt:page',
|
|
153
|
-
properties: [
|
|
154
|
-
{ name: 'jcr:title', value: 'Test Page', language: 'en' },
|
|
155
|
-
{ name: 'j:templateName', value: 'simple' },
|
|
156
|
-
],
|
|
157
|
-
}).then(() => {
|
|
158
|
-
addNode({
|
|
159
|
-
parentPathOrId: `/sites/${SITE_KEY}/home/testPage/pagecontent`,
|
|
160
|
-
name: 'myComponent',
|
|
161
|
-
primaryNodeType: 'ns:myComponentType',
|
|
162
|
-
properties: [
|
|
163
|
-
{ name: 'title', value: 'Hello World', language: 'en' },
|
|
164
|
-
{ name: 'subtitle', value: 'A subtitle', language: 'en' },
|
|
165
|
-
],
|
|
166
|
-
})
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
publishAndWaitJobEnding(`/sites/${SITE_KEY}`, ['en', 'fr'])
|
|
170
|
-
cy.logout()
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
after(() => {
|
|
174
|
-
cy.login()
|
|
175
|
-
deleteSite(SITE_KEY)
|
|
176
|
-
cy.logout()
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
beforeEach(() => cy.login())
|
|
180
|
-
afterEach(() => cy.logout())
|
|
181
|
-
|
|
182
|
-
it('renders the component in live mode', () => {
|
|
183
|
-
cy.visit(`/sites/${SITE_KEY}/home/testPage.html`)
|
|
184
|
-
cy.get('[data-testid="my-component"]').should('be.visible')
|
|
185
|
-
cy.contains('Hello World')
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
it('renders the component in FR locale', () => {
|
|
189
|
-
cy.visit(`/fr/sites/${SITE_KEY}/home/testPage.html`)
|
|
190
|
-
cy.get('[data-testid="my-component"]').should('be.visible')
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
it('renders the component in preview mode', () => {
|
|
194
|
-
cy.visit(`/cms/render/default/en/sites/${SITE_KEY}/home/testPage.html`)
|
|
195
|
-
cy.get('[data-testid="my-component"]').should('be.visible')
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
```
|
|
161
|
+
### Happy path
|
|
199
162
|
|
|
200
|
-
|
|
163
|
+
Validate the component renders correctly in live, preview, and at least one secondary locale.
|
|
201
164
|
|
|
202
|
-
|
|
203
|
-
import { addNode, createSite, deleteSite, publishAndWaitJobEnding } from '@jahia/cypress'
|
|
204
|
-
import { SITE_KEY, TEMPLATE_SET } from '../../support/constants'
|
|
205
|
-
|
|
206
|
-
describe('<ComponentName> — authorization', () => {
|
|
207
|
-
before(() => {
|
|
208
|
-
cy.login()
|
|
209
|
-
createSite(SITE_KEY, {
|
|
210
|
-
templateSet: TEMPLATE_SET,
|
|
211
|
-
locale: 'en',
|
|
212
|
-
serverName: 'localhost',
|
|
213
|
-
})
|
|
214
|
-
publishAndWaitJobEnding(`/sites/${SITE_KEY}`, ['en'])
|
|
215
|
-
cy.logout()
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
after(() => {
|
|
219
|
-
cy.login()
|
|
220
|
-
deleteSite(SITE_KEY)
|
|
221
|
-
cy.logout()
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
it('live page is publicly accessible without login', () => {
|
|
225
|
-
// No cy.login() — verify anonymous access
|
|
226
|
-
cy.visit(`/sites/${SITE_KEY}/home.html`)
|
|
227
|
-
cy.get('body').should('be.visible')
|
|
228
|
-
// Should NOT redirect to login
|
|
229
|
-
cy.url().should('not.include', '/login')
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
it('default workspace requires authentication', () => {
|
|
233
|
-
cy.visit(`/cms/render/default/en/sites/${SITE_KEY}/home.html`)
|
|
234
|
-
// Unauthenticated → redirected to login or 401
|
|
235
|
-
cy.url().should('satisfy', (url: string) =>
|
|
236
|
-
url.includes('/login') || url.includes('/logout')
|
|
237
|
-
)
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('admin can access jcontent for the site', () => {
|
|
241
|
-
cy.login()
|
|
242
|
-
cy.visit(`/jahia/jcontent/${SITE_KEY}/en/pages`)
|
|
243
|
-
cy.get('body').should('be.visible')
|
|
244
|
-
cy.url().should('not.include', '/login')
|
|
245
|
-
cy.logout()
|
|
246
|
-
})
|
|
247
|
-
})
|
|
248
|
-
```
|
|
165
|
+
### Authorization
|
|
249
166
|
|
|
250
|
-
|
|
167
|
+
Validate anonymous versus authenticated access where relevant.
|
|
251
168
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
describe('<ComponentName> — edge cases', () => {
|
|
257
|
-
before(() => {
|
|
258
|
-
cy.login()
|
|
259
|
-
createSite(SITE_KEY, {
|
|
260
|
-
templateSet: TEMPLATE_SET,
|
|
261
|
-
locale: 'en',
|
|
262
|
-
serverName: 'localhost',
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
// Seed a component with only mandatory fields — all optionals missing
|
|
266
|
-
addNode({
|
|
267
|
-
parentPathOrId: `/sites/${SITE_KEY}/home`,
|
|
268
|
-
name: 'testPage',
|
|
269
|
-
primaryNodeType: 'jnt:page',
|
|
270
|
-
properties: [
|
|
271
|
-
{ name: 'jcr:title', value: 'Edge Case Page', language: 'en' },
|
|
272
|
-
{ name: 'j:templateName', value: 'simple' },
|
|
273
|
-
],
|
|
274
|
-
}).then(() => {
|
|
275
|
-
addNode({
|
|
276
|
-
parentPathOrId: `/sites/${SITE_KEY}/home/testPage/pagecontent`,
|
|
277
|
-
name: 'emptyComponent',
|
|
278
|
-
primaryNodeType: 'ns:myComponentType',
|
|
279
|
-
// Only mandatory properties — no optional ones
|
|
280
|
-
properties: [
|
|
281
|
-
{ name: 'title', value: 'Minimal', language: 'en' },
|
|
282
|
-
],
|
|
283
|
-
})
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
publishAndWaitJobEnding(`/sites/${SITE_KEY}`, ['en'])
|
|
287
|
-
cy.logout()
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
after(() => {
|
|
291
|
-
cy.login()
|
|
292
|
-
deleteSite(SITE_KEY)
|
|
293
|
-
cy.logout()
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
it('renders without errors when optional fields are empty', () => {
|
|
297
|
-
cy.visit(`/sites/${SITE_KEY}/home/testPage.html`)
|
|
298
|
-
// No JS errors
|
|
299
|
-
cy.on('uncaught:exception', () => false) // log but don't fail on known framework noise
|
|
300
|
-
cy.get('[data-testid="my-component"]').should('exist')
|
|
301
|
-
// Optional subtitle should not render a broken element
|
|
302
|
-
cy.get('[data-testid="subtitle"]').should('not.exist')
|
|
303
|
-
})
|
|
304
|
-
|
|
305
|
-
it('page does not return 500 for any seeded content', () => {
|
|
306
|
-
cy.request(`/sites/${SITE_KEY}/home/testPage.html`).its('status').should('eq', 200)
|
|
307
|
-
})
|
|
308
|
-
})
|
|
309
|
-
```
|
|
169
|
+
### Edge cases
|
|
170
|
+
|
|
171
|
+
Seed only mandatory fields and verify missing optional values do not cause broken renders or 500s.
|
|
310
172
|
|
|
311
173
|
---
|
|
312
174
|
|
|
313
|
-
## Step 5 —
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
175
|
+
## Step 5 — Seed content predictably
|
|
176
|
+
|
|
177
|
+
Use `createSite`, `addNode`, and `publishAndWaitJobEnding` from `@jahia/cypress`.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { addNode, createSite, deleteSite, publishAndWaitJobEnding } from '@jahia/cypress';
|
|
181
|
+
|
|
182
|
+
addNode({
|
|
183
|
+
parentPathOrId: `/sites/${SITE_KEY}/home/testPage/pagecontent`,
|
|
184
|
+
name: 'myComponent',
|
|
185
|
+
primaryNodeType: 'ns:myComponentType',
|
|
186
|
+
properties: [
|
|
187
|
+
{ name: 'title', value: 'Hello World', language: 'en' },
|
|
188
|
+
],
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
publishAndWaitJobEnding(`/sites/${SITE_KEY}`, ['en']);
|
|
333
192
|
```
|
|
334
193
|
|
|
335
|
-
|
|
194
|
+
Prefer fixture files over long inline GraphQL strings when seeding becomes complex.
|
|
336
195
|
|
|
337
196
|
```typescript
|
|
338
197
|
cy.apollo({
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
})
|
|
198
|
+
mutationFile: 'graphql/mutation/seedMyComponent.graphql',
|
|
199
|
+
variables: { parentPath: `/sites/${SITE_KEY}/home/testPage/pagecontent` },
|
|
200
|
+
});
|
|
342
201
|
```
|
|
343
202
|
|
|
344
203
|
---
|
|
345
204
|
|
|
346
|
-
## Step 6 —
|
|
205
|
+
## Step 6 — Use stable selectors
|
|
347
206
|
|
|
348
|
-
CSS Modules hash class names
|
|
207
|
+
CSS Modules hash class names, so do not target final generated class names directly.
|
|
349
208
|
|
|
350
209
|
```typescript
|
|
351
|
-
//
|
|
352
|
-
cy.get('
|
|
210
|
+
// Better: add data-testid in the component
|
|
211
|
+
cy.get('[data-testid="hero-banner"]').should('be.visible');
|
|
353
212
|
|
|
354
|
-
//
|
|
355
|
-
cy.get('[class*="_card_"]').
|
|
356
|
-
cy.get('[class*="_card_"]').first().click()
|
|
357
|
-
|
|
358
|
-
// ✅ Better — add data-testid to the component and target that
|
|
359
|
-
cy.get('[data-testid="hero-banner"]').should('be.visible')
|
|
213
|
+
// Acceptable fallback for CSS Modules
|
|
214
|
+
cy.get('[class*="_card_"]').first().click();
|
|
360
215
|
```
|
|
361
216
|
|
|
362
|
-
|
|
217
|
+
Preferred approach in production views:
|
|
363
218
|
|
|
364
219
|
```tsx
|
|
365
|
-
// In default.server.tsx
|
|
366
220
|
<section data-testid="hero-banner" className={styles.hero}>
|
|
367
221
|
```
|
|
368
222
|
|
|
369
223
|
---
|
|
370
224
|
|
|
371
|
-
## Step 7 —
|
|
225
|
+
## Step 7 — Organize support code cleanly
|
|
372
226
|
|
|
373
|
-
|
|
227
|
+
- Keep `cypress/support/commands.ts` focused on command registration.
|
|
228
|
+
- Move larger helper implementations into small modules under `cypress/support/`.
|
|
229
|
+
- Put run-level cleanup in `support/e2e.ts` so repeated runs stay idempotent.
|
|
230
|
+
- Avoid large copy-pasted setup blocks across spec files.
|
|
374
231
|
|
|
375
|
-
|
|
376
|
-
// EN (default)
|
|
377
|
-
cy.visit(`/sites/${SITE_KEY}/home/testPage.html`)
|
|
232
|
+
---
|
|
378
233
|
|
|
379
|
-
|
|
380
|
-
cy.visit(`/fr/sites/${SITE_KEY}/home/testPage.html`)
|
|
234
|
+
## Guardrails
|
|
381
235
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
236
|
+
1. Never test against production data — always create isolated sites and fixtures.
|
|
237
|
+
2. Always clean up created sites and users in `after()` hooks.
|
|
238
|
+
3. Publish seeded content before asserting live renders.
|
|
239
|
+
4. Prefer `data-testid` selectors over CSS classes.
|
|
240
|
+
5. Avoid flaky `cy.wait(ms)` calls — use polling helpers or explicit conditions instead.
|
|
241
|
+
6. Test both the happy path and the main failure/empty-state paths.
|
|
242
|
+
7. Verify at least one secondary locale such as FR when the component is localized.
|
|
385
243
|
|
|
386
244
|
---
|
|
387
245
|
|
|
388
|
-
##
|
|
389
|
-
|
|
390
|
-
| Function | Import | Purpose |
|
|
391
|
-
|---|---|---|
|
|
392
|
-
| `cy.login()` | built-in command | Log in as root (uses `SUPER_USER_PASSWORD` env var, default `root1234`) |
|
|
393
|
-
| `cy.logout()` | built-in command | Log out |
|
|
394
|
-
| `cy.apollo({ mutationFile, variables })` | built-in command | Execute a GraphQL mutation from a fixture file |
|
|
395
|
-
| `cy.runProvisioningScript({ script })` | built-in command | Run a Jahia provisioning YAML |
|
|
396
|
-
| `createSite(siteKey, options)` | `@jahia/cypress` | Create a site programmatically |
|
|
397
|
-
| `deleteSite(siteKey)` | `@jahia/cypress` | Delete a site (use in `after`) |
|
|
398
|
-
| `addNode(variables)` | `@jahia/cypress` | Create a JCR node via GraphQL |
|
|
399
|
-
| `publishAndWaitJobEnding(path, locales?)` | `@jahia/cypress` | Publish content and block until the job completes |
|
|
400
|
-
| `uploadFile(path, target, name, mimeType)` | `@jahia/cypress` | Upload a file to the JCR |
|
|
246
|
+
## Running tests
|
|
401
247
|
|
|
402
|
-
|
|
248
|
+
```bash
|
|
249
|
+
# From the module root
|
|
250
|
+
yarn build && yarn jahia-deploy
|
|
403
251
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
templateSet: 'my-module', // module name
|
|
407
|
-
locale: 'en', // default locale
|
|
408
|
-
languages: 'en,fr', // all activated locales (comma-separated)
|
|
409
|
-
serverName: 'localhost',
|
|
410
|
-
})
|
|
252
|
+
# Then run Cypress
|
|
253
|
+
cd tests && yarn e2e:ci
|
|
411
254
|
```
|
|
412
255
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
## Common pitfalls
|
|
416
|
-
|
|
417
|
-
| Pitfall | Consequence |
|
|
418
|
-
|---|---|
|
|
419
|
-
| Missing `publishAndWaitJobEnding` after seeding | Tests run against unpublished content; live page returns stale or empty render |
|
|
420
|
-
| Targeting hashed CSS Module class names directly | Tests break on every rebuild |
|
|
421
|
-
| Skipping FR assertions | i18n regressions go undetected |
|
|
422
|
-
| No `after` hook to delete the site | Test sites accumulate on the Jahia instance and pollute subsequent runs |
|
|
423
|
-
| Running `createSite` without first deleting — no teardown in prior run | Site creation fails because the key already exists |
|
|
424
|
-
| Relying on a pre-existing site instead of seeding | Tests are not self-contained and fail on a clean Jahia instance |
|
|
425
|
-
| Not wrapping login/logout in `beforeEach`/`afterEach` | Session bleeds across tests; order-dependent failures |
|
|
256
|
+
Use `yarn e2e:debug` locally when you need the interactive runner.
|
|
426
257
|
|
|
427
258
|
---
|
|
428
259
|
|
|
429
|
-
##
|
|
430
|
-
|
|
431
|
-
```bash
|
|
432
|
-
# Against local Jahia (http://localhost:8080)
|
|
433
|
-
cd tests
|
|
434
|
-
yarn e2e:debug # opens Cypress UI
|
|
435
|
-
yarn e2e:ci # headless, for CI
|
|
436
|
-
```
|
|
260
|
+
## Related skills
|
|
437
261
|
|
|
438
|
-
|
|
262
|
+
- `/jahia-dev-build-component` — build the component being tested
|
|
263
|
+
- `/jahia-dev-accessibility` — catch semantic and keyboard issues before or after writing e2e tests
|
|
264
|
+
- `/jahia-dev-create-page-template` — create the page templates that Cypress will exercise
|
|
439
265
|
|
|
440
|
-
```bash
|
|
441
|
-
# From module root
|
|
442
|
-
yarn build && yarn jahia-deploy
|
|
443
|
-
# Then run tests
|
|
444
|
-
cd tests && yarn e2e:ci
|
|
445
|
-
```
|