Skip to content

Conversation

Strift
Copy link
Contributor

@Strift Strift commented Oct 9, 2025

what

Refactor Cypress tests to:

  • make seeding strategy more resilient to Strapi UI changes
  • improve existing tests cases

how

  • TBD

Summary by CodeRabbit

  • New Features

    • Playground now auto-seeds an admin user on startup.
    • Playground admin UI no longer auto-opens.
    • Renamed display name “restaurant” → “Restaurant” in playground.
  • Tests

    • Added comprehensive E2E test suites for plugin features and permissions.
    • Removed outdated admin and roles E2E suites.
    • Upgraded Cypress to v15.3.0; test helpers and commands updated.
  • Chores

    • New playground setup scripts and streamlined dev scripts.
    • .gitignore updated to include build artifacts (dist) and refine playground DB handling.
  • CI

    • CI matrix consolidated to Node 20.

@Strift Strift added the maintenance Anything related to maintenance (CI, tests, refactoring...) label Oct 9, 2025
Copy link

coderabbitai bot commented Oct 9, 2025

Walkthrough

Adds Cursor rule configs; updates .gitignore entries; modifies Cypress test suite (adds features and permissions tests, removes admin and roles tests), updates support commands and utils; bumps Cypress and scripts; adjusts playground config and scripts; capitalizes a content-type displayName; and replaces playground register with an async bootstrap that seeds an admin user.

Changes

Cohort / File(s) Summary
Cursor rule configs
\.cursor/rules/cypress.mdc, \.cursor/rules/general.mdc
Added rule files with YAML front matter; cypress rule globs set to cypress/**.* with alwaysApply: false; general rules include description, globs, and alwaysApply: true.
Repo ignore updates
\.gitignore, playground/.gitignore
Enabled ignoring .tmp, added dist, removed blanket ignore for playground DB, explicitly kept playground/.tmp/data.db; normalized newline in playground/.gitignore.
Cypress E2E suites
cypress/e2e/features.cy.js, cypress/e2e/permissions.cy.js, cypress/e2e/admin.cy.js (removed), cypress/e2e/roles.cy.js (removed)
Added comprehensive feature and permissions test suites; removed legacy admin.cy.js and roles.cy.js. Tests cover login, settings, collections indexing, content hooks, counts, and permission gating.
Cypress support & utils
cypress/support/commands.js, cypress/e2e/utils.js
commands.js: login signature extended to accept shouldContain and uses it in post-login assertion. utils.js: added exported STRAPI_ADMIN_ROLES mapping.
Tooling & scripts
package.json, playground/package.json
Added playground:setup script; simplified playground:dev; playground package.json adds setup to copy seeded DB. Bumped cypress dev dependency to ^15.3.0.
Playground config
playground/config/admin.js
Added autoOpen: false to admin config.
Playground content type
playground/src/api/restaurant/content-types/restaurant/schema.json
Capitalized info.displayName from "restaurant" to "Restaurant".
Playground bootstrap seeding
playground/src/index.js
Replaced no-op register with async bootstrap({ strapi }) that finds/creates an admin user, assigns super-admin role if creating, updates password when default, and logs outcomes with error handling.
CI workflows
.github/workflows/...
Reduced Node.js matrix to Node 20 in multiple workflows and standardized array quoting in triggers.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Process as Node.js Process
  participant Strapi as Strapi (bootstrap)
  participant AdminSvc as admin::user service
  participant RoleSvc as Role service

  Process->>Strapi: start()
  Strapi->>Strapi: bootstrap({ strapi }) — seed admin
  Strapi->>AdminSvc: findOne({ email })
  alt admin not found
    Strapi->>RoleSvc: find \"strapi-super-admin\" role
    Strapi->>AdminSvc: create({ email, password, roles:[super-admin] })
    AdminSvc-->>Strapi: created user
  else admin exists
    Strapi->>AdminSvc: if password === 'password' -> update(userId, { password })
    AdminSvc-->>Strapi: updated (optional)
  end
  Strapi-->>Process: bootstrap complete (log success/failure)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A nibble of tests, I hop through the code,
New seeds in the meadow where admin users bode.
I tuck away tmp, keep the DB in sight,
Cypress grows taller beneath moonlit night.
Bootstrap done — I twitch my nose, hooray! 🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly indicates that this PR is a chore focused on refactoring the Cypress tests, which matches the extensive additions, deletions, and updates to test suites and supporting files. It is clear and specific enough for a reviewer to understand the primary change without listing every detail. The conventional “chore:” prefix appropriately signals a maintenance update.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/refactor-cypress-tests

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28fc9bd and 6df24e8.

📒 Files selected for processing (3)
  • .github/workflows/beta-tests.yml (2 hunks)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cypress-run
🔇 Additional comments (1)
.github/workflows/tests.yml (1)

20-44: Confirm dropping Node 18 coverage

Reducing the matrices to only Node 20 stops CI from exercising Node 18. If the plugin (or Strapi versions we target) still advertises Node 18 support, this drops a safety net and could mask regressions for those users. Please confirm that we are officially discontinuing Node 18 support—or keep the dual matrix until the minimum supported Node version is updated elsewhere (docs, package engines, release notes, etc.).


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (9)
cypress/support/commands.js (1)

38-38: Consider the impact of removing the timeout option.

The timeout option was removed from cy.get('form'). While the current implementation uses should('be.visible') which has its own timeout, ensure this change doesn't affect tests running against slower environments.

cypress/e2e/permissions.cy.js (4)

105-113: Scope selection with find; avoid nested get

get(...) resets the subject to document. Use find to scope to the nav and streamline the click flow.

-      cy.get('nav')
-        .get('a[aria-label="Meilisearch"]', { timeout: 10000 })
-        .should('be.visible')
-      cy.get('nav')
-        .get('a[aria-label="Meilisearch"]', { timeout: 10000 })
-        .click()
+      cy.get('nav')
+        .find('a[aria-label="Meilisearch"]', { timeout: 10000 })
+        .should('be.visible')
+        .click()

156-161: Prefer cy.contains/find over jQuery :contains inside cy.get

Selectors like tr:contains("user") inside get are brittle. Use cy.contains to locate the row, then scope with find.

-      cy.get('tr:contains("user") button[role="checkbox"]').first().click()
+      cy.contains('tr', 'user').find('button[role="checkbox"]').first().click()
@@
-      cy.get('tr:contains("user")').contains('Yes').should('be.visible')
-      cy.get('tr:contains("user")').contains('1 / 1').should('be.visible')
-      cy.get('tr:contains("user")').contains('Hooked').should('be.visible')
+      cy.contains('tr', 'user').should('contain.text', 'Yes')
+      cy.contains('tr', 'user').should('contain.text', '1 / 1')
+      cy.contains('tr', 'user').should('contain.text', 'Hooked')
@@
-      cy.get('tr:contains(user)').first().contains('Yes').should('be.visible')
-      cy.get('tr:contains(user)')
-        .first()
-        .contains('Hooked')
-        .should('be.visible')
-      cy.get('tr:contains(user)').first().contains('button', 'Update').click()
+      cy.contains('tr', 'user').should('contain.text', 'Yes')
+      cy.contains('tr', 'user').should('contain.text', 'Hooked')
+      cy.contains('tr', 'user').contains('button', 'Update').click()
@@
-      cy.get('tr:contains(user)').first().contains('Yes').should('be.visible')
-      cy.get('tr:contains(user)')
-        .first()
-        .contains('Hooked')
-        .should('be.visible')
-      cy.get('tr:contains(user)')
-        .first()
-        .get('button[role="checkbox"]')
-        .first()
-        .click()
+      cy.contains('tr', 'user').should('contain.text', 'Yes')
+      cy.contains('tr', 'user').should('contain.text', 'Hooked')
+      cy.contains('tr', 'user').find('button[role="checkbox"]').first().click()
@@
-      cy.get('tr:contains(user)')
-        .first()
-        .contains('Reload needed')
-        .should('be.visible')
+      cy.contains('tr', 'user').contains('Reload needed').should('be.visible')

Also applies to: 166-177, 223-232, 271-287


186-187: Standardize negative assertions for buttons

Replace "root().should('not.contain', 'button:contains(...)')" with direct non-existence checks.

-      cy.root().should('not.contain', 'button:contains("Update")')
+      cy.contains('button', 'Update').should('not.exist')
@@
-      cy.root().should('not.contain', 'button:contains("Save")')
+      cy.contains('button', 'Save').should('not.exist')

Apply similarly at the other cited locations.

Also applies to: 192-193, 236-237, 265-266, 335-336, 340-341


35-43: Extract helpers to Cypress commands

loginUser and visitPluginPage are duplicated across suites. Move them into cypress/support/commands.js (or a shared util) for reuse and stability.

I can provide a commands.js snippet if helpful.

Also applies to: 46-50

cypress/e2e/features.cy.js (3)

69-70: Use cy.contains for text instead of jQuery :contains inside get

Prefer cy.contains('button', 'Settings') over cy.get('button:contains("Settings")') for clarity and reliability.

-      cy.get('button:contains("Settings")').click()
+      cy.contains('button', 'Settings').click()
@@
-      cy.get('button:contains("Settings")').click()
+      cy.contains('button', 'Settings').click()

Apply similarly at the other cited locations.

Also applies to: 86-87, 94-98, 111-118, 135-140


35-44: Use forEach for side-effects (avoid map)

map is unused; forEach is clearer for iterative assertions.

-  const checkCollectionContent = ({ rowNb, contains }) => {
+  const checkCollectionContent = ({ rowNb, contains }) => {
     const row = `table[role='grid'] tbody tr:nth-child(${rowNb})`
-    contains.map(value =>
+    contains.forEach(value =>
       cy
         .get(row, {
           timeout: 10000,
         })
         .contains(value, { timeout: 10000 }),
-    )
+    )
   }

46-64: Avoid static waits in cy.session validate

Rely on content-based waits only. Drop cy.wait(1000) to reduce flakiness.

-          cy.wait(1000)
           cy.contains('Hello User who can manage Meilisearch').should(
             'be.visible',
           )
playground/package.json (1)

7-7: Ensure target dir exists and overwrite deterministically

Plain cp will fail if .tmp is missing. Create the directory and overwrite.

-    "setup": "cp pre-seeded-database.db .tmp/data.db",
+    "setup": "mkdir -p .tmp && cp -f pre-seeded-database.db .tmp/data.db",

If Windows support is required, consider using shx: "setup": "shx mkdir -p .tmp && shx cp -f pre-seeded-database.db .tmp/data.db".

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between faa6492 and 28fc9bd.

⛔ Files ignored due to path filters (5)
  • playground/.tmp/data.db is excluded by !**/*.db
  • playground/pre-seeded-database.db is excluded by !**/*.db
  • playground/types/generated/contentTypes.d.ts is excluded by !**/generated/**
  • playground/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • .cursor/rules/cypress.mdc (1 hunks)
  • .cursor/rules/general.mdc (1 hunks)
  • .gitignore (2 hunks)
  • cypress/e2e/admin.cy.js (0 hunks)
  • cypress/e2e/features.cy.js (1 hunks)
  • cypress/e2e/permissions.cy.js (1 hunks)
  • cypress/e2e/roles.cy.js (0 hunks)
  • cypress/e2e/utils.js (1 hunks)
  • cypress/support/commands.js (1 hunks)
  • package.json (2 hunks)
  • playground/.gitignore (1 hunks)
  • playground/config/admin.js (1 hunks)
  • playground/package.json (1 hunks)
  • playground/src/api/restaurant/content-types/restaurant/schema.json (1 hunks)
  • playground/src/index.js (1 hunks)
💤 Files with no reviewable changes (2)
  • cypress/e2e/admin.cy.js
  • cypress/e2e/roles.cy.js
🧰 Additional context used
🪛 Biome (2.1.2)
cypress/e2e/utils.js

[error] 1-6: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cypress-run
🔇 Additional comments (11)
.cursor/rules/cypress.mdc (1)

1-6: LGTM!

The Cursor rule configuration is well-structured and provides helpful context for developers working with Cypress tests.

package.json (1)

10-11: Good separation of setup and dev scripts.

The new playground:setup script and the simplified playground:dev script improve the workflow clarity and align with the seeding strategy refactor.

.cursor/rules/general.mdc (1)

1-7: LGTM!

The general rule configuration appropriately documents the project's package manager conventions.

playground/src/api/restaurant/content-types/restaurant/schema.json (1)

7-7: LGTM!

The capitalization of the display name improves the UI presentation and follows proper naming conventions.

.gitignore (2)

75-75: LGTM!

Enabling the .tmp directory ignore follows standard practices for temporary files.


136-139: LGTM!

The addition of dist to the ignore list and the exception for playground/.tmp/data.db properly support the new seeding strategy. The exception ensures the pre-seeded database remains tracked for the playground setup workflow.

playground/.gitignore (1)

134-134: LGTM!

The trailing newline ensures proper file formatting.

cypress/support/commands.js (1)

33-51: No cy.login( calls found. Manually verify any login invocations pass the required shouldContain argument.

cypress/e2e/features.cy.js (1)

141-165: Sync after toggles: rely on intercepted aliases for each row

Good use of intercepts. Minor: ensure waits pair to each click to avoid race across loop iterations.

Consider moving waits inside the click branch (already done) and adding a short assertion per row after @fetchCollections completes, as you did below. LGTM otherwise.

playground/config/admin.js (1)

2-2: autoOpen: false is appropriate for CI/test runs

Prevents auto-opening the admin UI during setup. LGTM.

Confirm this property is supported in your Strapi version (5.9.0). If needed, we can quickly check docs.

playground/src/index.js (1)

7-17: Use Strapi v5 APIs for queries and updates

  • Replace strapi.query('admin::user').findOne with strapi.db.query('admin::user').findOne
  • Replace adminUserService.updateById(adminUser.id, {...}) with the v5 update signature, e.g.:
    await strapi.db.query('admin::user').update({
      where: { id: adminUser.id },
      data: { password: 'password' },
    });

Comment on lines +77 to +81
cy.get('div[role="status').contains('success').should('be.visible')
cy.get('div[role="status')
.contains('Credentials successfully updated')
.should('be.visible')
cy.removeNotifications()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Invalid attribute selector; fix bracket/quote

Selector 'div[role="status' is malformed; tests will fail to locate the status element.

-      cy.get('div[role="status').contains('success').should('be.visible')
-      cy.get('div[role="status')
+      cy.get('div[role="status"]').contains('success').should('be.visible')
+      cy.get('div[role="status"]')
         .contains('Credentials successfully updated')
         .should('be.visible')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cy.get('div[role="status').contains('success').should('be.visible')
cy.get('div[role="status')
.contains('Credentials successfully updated')
.should('be.visible')
cy.removeNotifications()
cy.get('div[role="status"]').contains('success').should('be.visible')
cy.get('div[role="status"]')
.contains('Credentials successfully updated')
.should('be.visible')
cy.removeNotifications()
🤖 Prompt for AI Agents
In cypress/e2e/features.cy.js around lines 77 to 81 the attribute selector is
malformed ('div[role="status' missing the closing quote and bracket), causing
the test to fail to find the status element; update both selectors to use the
correct syntax div[role="status"] (e.g.,
cy.get('div[role="status"]').contains('success').should('be.visible') and
cy.get('div[role="status"]').contains('Credentials successfully
updated').should('be.visible') so the attribute selector is properly closed.

Comment on lines +265 to +267
cy.get('input[name="title').type('The slimy snail')
cy.get('form').contains('button', 'Save').click()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix selector for title input

Missing closing bracket in attribute selector.

-      cy.get('input[name="title').type('The slimy snail')
+      cy.get('input[name="title"]').type('The slimy snail')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cy.get('input[name="title').type('The slimy snail')
cy.get('form').contains('button', 'Save').click()
cy.get('input[name="title"]').type('The slimy snail')
cy.get('form').contains('button', 'Save').click()
🤖 Prompt for AI Agents
In cypress/e2e/features.cy.js around lines 265 to 267 the attribute selector for
the title input is missing a closing quote/bracket: update the selector to
correctly target the input element (e.g., input[name="title"] ), then run the
test to ensure the .type('The slimy snail') call targets the field before
clicking the Save button.

Comment on lines +71 to +75
it('cannot see the plugin in the sidepanel', () => {
cy.visit(`${adminUrl}`)
cy.get('nav').should('not.contain', 'a[aria-label="Meilisearch"]')
})

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix negative existence assertion in sidepanel check

Using "not.contain" with a selector string checks text, not element presence. Assert element non-existence instead.

Apply this diff:

-    cy.visit(`${adminUrl}`)
-    cy.get('nav').should('not.contain', 'a[aria-label="Meilisearch"]')
+    cy.visit(`${adminUrl}`)
+    cy.get('nav').find('a[aria-label="Meilisearch"]').should('not.exist')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('cannot see the plugin in the sidepanel', () => {
cy.visit(`${adminUrl}`)
cy.get('nav').should('not.contain', 'a[aria-label="Meilisearch"]')
})
it('cannot see the plugin in the sidepanel', () => {
cy.visit(`${adminUrl}`)
cy.get('nav').find('a[aria-label="Meilisearch"]').should('not.exist')
})
🤖 Prompt for AI Agents
In cypress/e2e/permissions.cy.js around lines 71 to 75, the test uses
cy.get('nav').should('not.contain', 'a[aria-label="Meilisearch"]'), which checks
text content rather than element presence; change it to assert element
non-existence by querying the link selector inside the nav and asserting it does
not exist (for example, use nav.find('a[aria-label="Meilisearch"]') and assert
.should('not.exist') or use cy.get('nav').within(...) and assert the selector
does not exist).

Comment on lines +115 to +125
it('cannot create an index', () => {
visitPluginPage()
// There are checkboxes in front of each collection
cy.root().should('not.contain', 'button[role="checkbox"]')
})

it('cannot change settings', () => {
visitPluginPage()
cy.root().should('not.contain', 'button:contains("Save")')
})
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use existence checks, not text-based "not.contain"

For "cannot create index" and "cannot change settings", assert element absence directly.

-      // There are checkboxes in front of each collection
-      cy.root().should('not.contain', 'button[role="checkbox"]')
+      // There are checkboxes in front of each collection
+      cy.get('button[role="checkbox"]').should('not.exist')
@@
-      cy.root().should('not.contain', 'button:contains("Save")')
+      cy.contains('button', 'Save').should('not.exist')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('cannot create an index', () => {
visitPluginPage()
// There are checkboxes in front of each collection
cy.root().should('not.contain', 'button[role="checkbox"]')
})
it('cannot change settings', () => {
visitPluginPage()
cy.root().should('not.contain', 'button:contains("Save")')
})
})
it('cannot create an index', () => {
visitPluginPage()
// There are checkboxes in front of each collection
cy.get('button[role="checkbox"]').should('not.exist')
})
it('cannot change settings', () => {
visitPluginPage()
cy.contains('button', 'Save').should('not.exist')
})
})
🤖 Prompt for AI Agents
In cypress/e2e/permissions.cy.js around lines 115 to 125, the tests use
text-based "not.contain" assertions to check absence; replace them with direct
DOM existence checks: for the "cannot create an index" test assert that the
checkbox button selector (button[role="checkbox"]) does not exist, and for the
"cannot change settings" test assert that the Save button (use a selector such
as a button containing the text "Save" or a specific save button selector) does
not exist; use Cypress existence assertions (e.g., .should('not.exist')) to
perform these checks.

Comment on lines +304 to +305
cy.get('button[role="checkbox"]').first().should('not.be.checked')
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

"be.checked" is invalid for button role checkboxes

The element is a button, not an input. Assert ARIA/data-state instead.

-      cy.get('button[role="checkbox"]').first().should('not.be.checked')
+      cy.get('button[role="checkbox"]')
+        .first()
+        .should($btn => {
+          const state = $btn.attr('aria-checked') || $btn.attr('data-state')
+          expect(state).to.not.eq('true')
+          expect(state).to.not.eq('checked')
+        })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cy.get('button[role="checkbox"]').first().should('not.be.checked')
})
cy.get('button[role="checkbox"]')
.first()
.should($btn => {
const state = $btn.attr('aria-checked') || $btn.attr('data-state')
expect(state).to.not.eq('true')
expect(state).to.not.eq('checked')
})
})
🤖 Prompt for AI Agents
In cypress/e2e/permissions.cy.js around lines 304-305, the test uses
.should('not.be.checked') on a button with role="checkbox" which is invalid for
non-input elements; replace that assertion with an ARIA/data-state check — e.g.,
assert the element's aria-checked attribute equals "false"
(cy.get(...).first().should('have.attr', 'aria-checked', 'false')) or, if your
implementation uses data-state, assert the appropriate data-state value (e.g.,
'unchecked').

Comment on lines +1 to +6
export const STRAPI_ADMIN_ROLES = {
SUPER_ADMIN: 1,
ADMIN: 2,
EDITOR: 3,
AUTHOR: 4,
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Centralized role constants are good, but verify ES module configuration.

The STRAPI_ADMIN_ROLES constant provides a clean centralized reference for role IDs across tests.

However, the static analysis error suggests this file may not be properly configured as an ES module. Ensure Cypress is configured to support ES modules (typically via cypress.config.js with specPattern and proper module settings).


🏁 Script executed:

#!/bin/bash
# Verify Cypress configuration supports ES modules
cat cypress.config.js 2>/dev/null || cat cypress.config.ts 2>/dev/null || echo "Cypress config not found"

Length of output: 836


Use CommonJS exports in cypress/e2e/utils.js

Cypress is configured as CommonJS, so replace the ES module export with:

-export const STRAPI_ADMIN_ROLES = {
+module.exports.STRAPI_ADMIN_ROLES = {
   SUPER_ADMIN: 1,
   ADMIN: 2,
   EDITOR: 3,
   AUTHOR: 4,
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const STRAPI_ADMIN_ROLES = {
SUPER_ADMIN: 1,
ADMIN: 2,
EDITOR: 3,
AUTHOR: 4,
}
module.exports.STRAPI_ADMIN_ROLES = {
SUPER_ADMIN: 1,
ADMIN: 2,
EDITOR: 3,
AUTHOR: 4,
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 1-6: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

🤖 Prompt for AI Agents
In cypress/e2e/utils.js lines 1-6, the file currently uses an ES module export;
change it to CommonJS by declaring the constant and exporting it with
module.exports. Replace the "export const STRAPI_ADMIN_ROLES = { ... }" syntax
with a plain const declaration and add "module.exports = { STRAPI_ADMIN_ROLES }"
so Cypress can require the value under CommonJS.

* @param {string} password
*/
const login = ({ adminUrl, email, password }) => {
const login = ({ adminUrl, email, password, shouldContain }) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add a default value or validation for the shouldContain parameter.

The new shouldContain parameter is not optional but lacks a default value. If callers don't provide this parameter, Line 47 will fail with undefined being passed to cy.contains().

Apply this diff to provide a sensible default:

-const login = ({ adminUrl, email, password, shouldContain }) => {
+const login = ({ adminUrl, email, password, shouldContain = 'Welcome' }) => {

Or add validation:

 const login = ({ adminUrl, email, password, shouldContain }) => {
+  if (!shouldContain) {
+    throw new Error('shouldContain parameter is required for login validation')
+  }
   cy.session(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const login = ({ adminUrl, email, password, shouldContain }) => {
const login = ({ adminUrl, email, password, shouldContain = 'Welcome' }) => {
🤖 Prompt for AI Agents
In cypress/support/commands.js around line 33, the login function signature
currently declares shouldContain without a default which can pass undefined into
cy.contains(); update the function to either give shouldContain a safe default
(e.g. shouldContain = undefined) and only call cy.contains(shouldContain) when
it's not undefined, or add an explicit validation at the start of the function
that checks if shouldContain is provided before invoking cy.contains; ensure
callers behavior is unchanged when shouldContain is supplied.

"babel-jest": "^29.7.0",
"concurrently": "^8.2.2",
"cypress": "^14.0.1",
"cypress": "^15.3.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify Cypress v15 compatibility.

The major version bump from Cypress ^14.0.1 to ^15.3.0 may introduce breaking changes. Ensure all existing tests remain compatible with Cypress v15.


🌐 Web query:

What are the breaking changes in Cypress v15 compared to v14?

💡 Result:

Short list of breaking changes introduced in Cypress v15 (changes you must address when moving from v14 → v15):

  • Node.js support: Node 18 and 23 are no longer supported; supported installers require Node 20, 22, 24+. [1][2]
  • Linux/glibc: Binaries no longer support Linux distributions with glibc older than 2.31. [1][2]
  • Firefox automation: Chrome DevTools Protocol support for Firefox removed. [1]
  • cy.exec result property rename: the returned property code → exitCode (update any .its('code') usages). [2]
  • cy.stub signature: removed support for the deprecated three-argument signature of cy.stub. [1]
  • API rename: Cypress.SelectorPlayground renamed to Cypress.ElementSelector (update any references). [3]

For migration details and examples see the official v15 changelog and migration guide. [1][2]

Sources: Cypress v15 changelog, v15 migration guide, Cypress blog post. [1][2][3]


Update tests for Cypress v15 breaking changes

  • Ensure CI runs on Node 20/22/24+ (Node 18 & 23 unsupported)
  • Verify Linux images use glibc ≥2.31
  • Remove Firefox CDP automation usage
  • Replace all cy.exec().its('code').its('exitCode')
  • Remove any 3-argument cy.stub() calls
  • Rename Cypress.SelectorPlaygroundCypress.ElementSelector

Comment on lines +28 to +35
// Update the password if it's not hashed (for existing users)
if (adminUser.password === 'password') {
const adminUserService = strapi.service('admin::user');
await adminUserService.updateById(adminUser.id, {
password: 'password'
});
console.log('✅ Admin user password updated with proper hash');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Plaintext password comparison is ineffective

adminUser.password is hashed; comparing to 'password' will never be true. Either always set a known password during seeding (if desired) or skip resetting existing users.

-        // Update the password if it's not hashed (for existing users)
-        if (adminUser.password === 'password') {
-          const adminUserService = strapi.service('admin::user');
-          await adminUserService.updateById(adminUser.id, {
-            password: 'password'
-          });
-          console.log('✅ Admin user password updated with proper hash');
-        }
+        // Keep existing admin user as-is to avoid overriding credentials
+        // If you need to reset the password for tests, do it explicitly and unconditionally.
+        // Example (optional):
+        // const adminUserService = strapi.service('admin::user');
+        // await adminUserService.updateById(adminUser.id, { password: 'password' });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Update the password if it's not hashed (for existing users)
if (adminUser.password === 'password') {
const adminUserService = strapi.service('admin::user');
await adminUserService.updateById(adminUser.id, {
password: 'password'
});
console.log('✅ Admin user password updated with proper hash');
}
// Keep existing admin user as-is to avoid overriding credentials
// If you need to reset the password for tests, do it explicitly and unconditionally.
// Example (optional):
// const adminUserService = strapi.service('admin::user');
// await adminUserService.updateById(adminUser.id, { password: 'password' });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance Anything related to maintenance (CI, tests, refactoring...)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant