Skip to content

Commit 9f3da2e

Browse files
committed
Scaffold API to seed using admin JWT
1 parent faa6492 commit 9f3da2e

File tree

5 files changed

+269
-17
lines changed

5 files changed

+269
-17
lines changed

.cursor/rules/cypress.mdc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
globs: cypress/**.*
3+
alwaysApply: false
4+
---
5+
6+
- The playground is already running, you don't need to start it

.cursor/rules/general.mdc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
- This project uses `ni` as package manager; use `nlx` to execute packages

cypress/e2e/new-roles.cy.js

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
const ADMIN_CREDENTIALS = {
2+
3+
password: 'password',
4+
}
5+
6+
const _USER_WITHOUT_ACCESS = {
7+
username: 'user_without_access',
8+
9+
password: 'strapiPassword',
10+
}
11+
12+
describe('wip test refactor', () => {
13+
// JWT token for admin panel operations (creating users, roles, permissions)
14+
let adminToken
15+
16+
// Long-lived API token for content management and plugin endpoints
17+
let apiToken
18+
19+
// Generate unique identifiers for this test run
20+
const timestamp = Date.now()
21+
const uniqueEmail = `test.user.${timestamp}@example.com`
22+
const uniqueRoleName = `Content Manager ${timestamp}`
23+
24+
before(() => {
25+
// Login as admin to get JWT token for admin panel operations
26+
cy.request({
27+
method: 'POST',
28+
url: 'http://localhost:1337/admin/login',
29+
body: {
30+
email: ADMIN_CREDENTIALS.email,
31+
password: ADMIN_CREDENTIALS.password,
32+
},
33+
})
34+
.then(response => {
35+
expect(response.status).to.eq(200)
36+
adminToken = response.body.data.token
37+
38+
// Get the API token created during bootstrap for content/plugin operations
39+
return cy.request({
40+
method: 'GET',
41+
url: 'http://localhost:1337/admin/api-tokens',
42+
headers: {
43+
Authorization: `Bearer ${adminToken}`,
44+
},
45+
})
46+
})
47+
.then(response => {
48+
expect(response.status).to.eq(200)
49+
console.log(
50+
'API Tokens response:',
51+
JSON.stringify(response.body, null, 2),
52+
)
53+
const cypressToken = response.body.data.find(
54+
token => token.name === 'cypress-test-token',
55+
)
56+
expect(cypressToken).to.exist
57+
apiToken = cypressToken.accessKey
58+
console.log('API Token set to:', apiToken)
59+
})
60+
})
61+
62+
it('should be able to create a new admin user with admin token', () => {
63+
// Create a new admin user using the admin JWT token
64+
cy.request({
65+
method: 'POST',
66+
url: 'http://localhost:1337/admin/users',
67+
headers: {
68+
Authorization: `Bearer ${adminToken}`,
69+
},
70+
body: {
71+
firstname: 'Test',
72+
lastname: 'User',
73+
email: uniqueEmail,
74+
roles: [2], // Editor role
75+
},
76+
}).then(response => {
77+
expect(response.status).to.eq(201)
78+
expect(response.body.data).to.have.property('id')
79+
expect(response.body.data.email).to.eq(uniqueEmail)
80+
expect(response.body.data.firstname).to.eq('Test')
81+
expect(response.body.data.lastname).to.eq('User')
82+
expect(response.body.data.roles).to.have.length(1)
83+
expect(response.body.data.roles[0].code).to.eq('strapi-editor')
84+
85+
// Store the created user ID for cleanup
86+
Cypress.env('createdUserId', response.body.data.id)
87+
})
88+
})
89+
90+
it('should be able to create a custom admin role with admin token', () => {
91+
// Create a new custom admin role using the admin JWT token
92+
cy.request({
93+
method: 'POST',
94+
url: 'http://localhost:1337/admin/roles',
95+
headers: {
96+
Authorization: `Bearer ${adminToken}`,
97+
},
98+
body: {
99+
name: uniqueRoleName,
100+
description: 'Can manage content but not system settings',
101+
},
102+
}).then(response => {
103+
expect(response.status).to.eq(201)
104+
expect(response.body.data).to.have.property('id')
105+
expect(response.body.data.name).to.eq(uniqueRoleName)
106+
expect(response.body.data.description).to.eq(
107+
'Can manage content but not system settings',
108+
)
109+
110+
// Store the created role ID for cleanup
111+
Cypress.env('createdRoleId', response.body.data.id)
112+
})
113+
})
114+
115+
it('should be able to list all admin users', () => {
116+
// List all admin users using the admin JWT token
117+
cy.request({
118+
method: 'GET',
119+
url: 'http://localhost:1337/admin/users',
120+
headers: {
121+
Authorization: `Bearer ${adminToken}`,
122+
},
123+
}).then(response => {
124+
expect(response.status).to.eq(200)
125+
expect(response.body.data).to.have.property('results')
126+
expect(response.body.data.results).to.be.an('array')
127+
expect(response.body.data.results.length).to.be.greaterThan(0)
128+
129+
// Should include our original admin user
130+
const adminUser = response.body.data.results.find(
131+
user => user.email === '[email protected]',
132+
)
133+
expect(adminUser).to.exist
134+
expect(adminUser.roles[0].code).to.eq('strapi-super-admin')
135+
})
136+
})
137+
138+
it('should be able to list all admin roles', () => {
139+
// List all admin roles using the admin JWT token
140+
cy.request({
141+
method: 'GET',
142+
url: 'http://localhost:1337/admin/roles',
143+
headers: {
144+
Authorization: `Bearer ${adminToken}`,
145+
},
146+
}).then(response => {
147+
expect(response.status).to.eq(200)
148+
expect(response.body.data).to.be.an('array')
149+
expect(response.body.data.length).to.be.greaterThan(2) // At least Super Admin, Editor, Author
150+
151+
// Should include the default roles
152+
const roleNames = response.body.data.map(role => role.name)
153+
expect(roleNames).to.include('Super Admin')
154+
expect(roleNames).to.include('Editor')
155+
expect(roleNames).to.include('Author')
156+
})
157+
})
158+
159+
// Cleanup tests - run after the main tests
160+
after(() => {
161+
// Clean up created user if it exists
162+
cy.window().then(win => {
163+
// Check if the alias exists before trying to get it
164+
if (Cypress.env('createdUserId')) {
165+
cy.request({
166+
method: 'DELETE',
167+
url: `http://localhost:1337/admin/users/${Cypress.env('createdUserId')}`,
168+
headers: {
169+
Authorization: `Bearer ${adminToken}`,
170+
},
171+
failOnStatusCode: false, // Don't fail if user doesn't exist
172+
})
173+
}
174+
})
175+
176+
// Clean up created role if it exists
177+
cy.window().then(win => {
178+
// Check if the alias exists before trying to get it
179+
if (Cypress.env('createdRoleId')) {
180+
cy.request({
181+
method: 'DELETE',
182+
url: `http://localhost:1337/admin/roles/${Cypress.env('createdRoleId')}`,
183+
headers: {
184+
Authorization: `Bearer ${adminToken}`,
185+
},
186+
failOnStatusCode: false, // Don't fail if role doesn't exist
187+
})
188+
}
189+
})
190+
})
191+
})

playground/config/admin.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module.exports = ({ env }) => ({
2+
autoOpen: false,
23
auth: {
34
secret: env('ADMIN_JWT_SECRET', '1eb9a218d26dc77bd39c11b0ab64b291'),
45
},

playground/src/index.js

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,67 @@
1-
'use strict';
2-
31
module.exports = {
4-
/**
5-
* An asynchronous register function that runs before
6-
* your application is initialized.
7-
*
8-
* This gives you an opportunity to extend code.
9-
*/
10-
register(/*{ strapi }*/) {},
2+
async bootstrap({ strapi }) {
3+
console.log('🚀 Bootstrap function called!');
4+
// Only run seeding in development/test environments
5+
if (process.env.NODE_ENV !== 'production') {
6+
try {
7+
console.log('🌱 Starting bootstrap seeding...');
8+
9+
// Check if admin user exists
10+
const adminUser = await strapi.query('admin::user').findOne({
11+
where: { email: '[email protected]' }
12+
});
13+
14+
if (!adminUser) {
15+
// Create admin user using Strapi's admin service
16+
const adminUserService = strapi.service('admin::user');
17+
const superAdminRole = await strapi.query('admin::role').findOne({
18+
where: { code: 'strapi-super-admin' }
19+
});
20+
21+
await adminUserService.create({
22+
firstname: 'Admin',
23+
lastname: 'User',
24+
25+
password: 'password',
26+
isActive: true,
27+
roles: [superAdminRole.id],
28+
});
29+
console.log('✅ Admin user created');
30+
} else {
31+
console.log('✅ Admin user already exists');
32+
// Update the password if it's not hashed (for existing users)
33+
if (adminUser.password === 'password') {
34+
const adminUserService = strapi.service('admin::user');
35+
await adminUserService.updateById(adminUser.id, {
36+
password: 'password'
37+
});
38+
console.log('✅ Admin user password updated with proper hash');
39+
}
40+
}
41+
42+
// Check if test API token exists
43+
const existingToken = await strapi.query('admin::api-token').findOne({
44+
where: { name: 'cypress-test-token' }
45+
});
46+
47+
if (!existingToken) {
48+
// Create API token for Cypress tests
49+
const tokenService = strapi.service('admin::api-token');
50+
const token = await tokenService.create({
51+
name: 'cypress-test-token',
52+
description: 'API token for Cypress tests',
53+
type: 'full-access',
54+
});
55+
console.log('✅ Cypress test token created:', token.accessKey);
56+
} else {
57+
console.log('✅ Cypress test token already exists');
58+
}
1159

12-
/**
13-
* An asynchronous bootstrap function that runs before
14-
* your application gets started.
15-
*
16-
* This gives you an opportunity to set up your data model,
17-
* run jobs, or perform some special logic.
18-
*/
19-
bootstrap(/*{ strapi }*/) {},
60+
console.log('🌱 Bootstrap seeding completed successfully');
61+
} catch (error) {
62+
console.error('❌ Bootstrap seeding failed:', error.message);
63+
console.error('Error details:', error);
64+
}
65+
}
66+
},
2067
};

0 commit comments

Comments
 (0)