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.
    • Display name “Restaurant” capitalized in playground.
  • Tests

    • Added new comprehensive E2E suites and removed legacy admin/roles suites.
    • Test helpers updated and login command adjusted.
    • Cypress upgraded to v15.3.0 and project ID added.
  • Chores

    • New playground setup scripts; dev script simplified.
    • .gitignore updated to preserve playground DB and ignore build artifacts.
  • CI

    • CI matrix narrowed to Node 20; playground DB seeding added and Cypress recording enabled.

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

coderabbitai bot commented Oct 9, 2025

Walkthrough

Adds cursor rule configs; updates .gitignore and playground ignores; replaces/adjusts Cypress test suites and support utilities; updates scripts, Cypress dependency, CI workflows and cypress.config; tweaks playground admin/schema; replaces playground register with an async bootstrap that seeds or updates an admin user.

Changes

Cohort / File(s) Summary
Cursor rule configs
.cursor/rules/cypress.mdc, .cursor/rules/general.mdc
New rule config files with YAML front matter. cypress.mdc sets globs to cypress/**.* and alwaysApply: false. general.mdc includes description, globs, and alwaysApply: true.
Gitignore & playground ignore
.gitignore, playground/.gitignore
Activated .tmp ignore, added dist, removed blanket playground DB ignore and added exception to keep playground/.tmp/data.db; normalized trailing newline in playground/.gitignore.
Cypress E2E suites
Added:
cypress/e2e/features.cy.js, cypress/e2e/permissions.cy.js
Removed:
cypress/e2e/admin.cy.js, cypress/e2e/roles.cy.js
Added comprehensive features.cy.js and permissions.cy.js test suites covering login, settings, collections, indexing, counts, content hooks and permission scenarios; removed admin.cy.js and roles.cy.js.
Cypress support & utils
cypress/support/commands.js, cypress/e2e/utils.js
commands.js: login accepts new shouldContain parameter and uses it for post-login assertion (signature updated). utils.js: added exported STRAPI_ADMIN_ROLES constant mapping role names to numeric IDs.
Cypress config
cypress.config.js
Added projectId: 'vu3oo5' to Cypress configuration returned by defineConfig.
Tooling & scripts
package.json, playground/package.json
Added new scripts (playground:setup, playground setup) for seeding playground DB, adjusted playground:dev invocation, and bumped dev dependency cypress to ^15.3.0.
Playground admin & schema
playground/config/admin.js, playground/src/api/restaurant/content-types/restaurant/schema.json
Added autoOpen: false to admin config; capitalized info.displayName from "restaurant" to "Restaurant".
Playground bootstrap seeding
playground/src/index.js
Replaced no-op register with async bootstrap({ strapi }) that finds or creates an admin user, assigns the super-admin role on create, updates default password if present, and logs success/failure.
CI workflows
.github/workflows/beta-tests.yml, .github/workflows/pre-release-tests.yml, .github/workflows/tests.yml, .github/workflows/cypress.yml
Reduced Node.js matrices to Node 20 only; standardized quoting; added a step to seed playground DB before Cypress e2e; enabled Cypress Cloud recording and recording key propagation; updated some env/port quoting.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer / CI
  participant Playground as Playground App
  participant AdminSvc as admin::user service
  participant RoleSvc as role service

  Dev->>Playground: start bootstrap({ strapi })
  Playground->>AdminSvc: findOne({ email })
  alt admin not found
    Playground->>RoleSvc: findOne({ code: "strapi-super-admin" })
    RoleSvc-->>Playground: role
    Playground->>AdminSvc: create({ email, password, roles: [role.id] })
    AdminSvc-->>Playground: user created
  else admin exists
    Playground->>AdminSvc: if password == 'password' -> update(userId, { password })
    AdminSvc-->>Playground: updated (optional)
  end
  Playground-->>Dev: log success / error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hop through code, a carrot in my paw,
New tests and seeds, I tidy up the draw.
.tmp tucked safe, the DB stays in sight,
Bootstrap hums softly to seed through the night.
A twitch, a cheer — the playground's set just right 🥕🐇

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 PR title "chore: refactor cypress tests" directly and accurately describes the primary focus of the changeset. The PR involves substantial restructuring of Cypress test files, including removing old test files (admin.cy.js, roles.cy.js), adding new test suites (features.cy.js, permissions.cy.js), and improving test infrastructure (commands.js, utils.js, cypress.config.js). The title is concise, specific to Cypress tests rather than overly broad, and clearly conveys the refactoring intent without vague language or noise.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 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

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' });

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: 0

🧹 Nitpick comments (1)
.github/workflows/cypress.yml (1)

70-73: Ensure CYPRESS_RECORD_KEY secret is properly configured in the GitHub repository.

The Cypress Cloud recording configuration requires CYPRESS_RECORD_KEY to be set in your GitHub repository secrets. Without this, the record: true and parallel: true settings may cause test failures or skip recording.

Verify that:

  1. CYPRESS_RECORD_KEY is configured in the repository's GitHub secrets
  2. The Cypress Cloud project is properly linked in cypress.config.js (or similar configuration)
  3. Your Cypress Cloud account has parallelization enabled for the project
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38b8fab and 5b7a7a7.

📒 Files selected for processing (1)
  • .github/workflows/cypress.yml (2 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). (2)
  • GitHub Check: cypress-run (2)
  • GitHub Check: cypress-run (1)
🔇 Additional comments (2)
.github/workflows/cypress.yml (2)

8-14: Parallel execution strategy is well-configured.

The matrix strategy with fail-fast: false and 2 parallel containers aligns with Cypress parallelization best practices. Tests will now distribute across 2 workers, improving CI feedback speed.


56-59: Script verification confirmed—no changes needed.

The yarn playground:setup script is correctly defined in root package.json as "yarn --cwd ./playground setup", which invokes the playground's setup script that copies the pre-seeded database file. The workflow is properly configured and the step ordering (directory creation before script execution) is correct.

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: 2

♻️ Duplicate comments (2)
cypress/e2e/features.cy.js (2)

77-81: Invalid attribute selector; fix bracket/quote.

Selector 'div[role="status' is malformed (missing closing quote and bracket); tests will fail to locate the status element.

Apply this diff to fix both selectors:

-      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')

267-267: Fix selector for title input.

Missing closing quote and bracket in attribute selector.

Apply this diff:

-      cy.get('input[name="title').type('The slimy snail')
+      cy.get('input[name="title"]').type('The slimy snail')
🧹 Nitpick comments (3)
.github/workflows/cypress.yml (1)

15-18: Standardize quote style in YAML (optional).

The quote style has been switched from single to double quotes for environment variables and port mappings. While both are valid YAML, maintaining consistency across the workflow is good practice. If your team has adopted a quote style convention, ensure it's applied consistently throughout the file.

Also applies to: 26-26

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

56-61: Replace arbitrary wait with element-based assertion.

The hard-coded cy.wait(1000) is brittle and can cause flakiness. The subsequent assertion already waits for the element to be visible.

Apply this diff to remove the unnecessary wait:

       validate() {
-        cy.wait(1000)
         cy.contains('Hello User who can manage Meilisearch').should(
           'be.visible',
         )
       },

171-198: Test interdependencies reduce isolation.

These tests explicitly assume previous tests have run (see comments on lines 170, 200). This creates fragile test ordering dependencies.

Consider extracting shared setup logic into helper functions or before() hooks to ensure each test can run independently. This improves maintainability and allows running individual tests during development.

Also applies to: 201-235

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94c1210 and b2c959d.

📒 Files selected for processing (2)
  • .github/workflows/cypress.yml (2 hunks)
  • cypress/e2e/features.cy.js (1 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 (3)
.github/workflows/cypress.yml (2)

49-52: LGTM! Seeding strategy improves test resilience.

The new "Seed playground database" step prepares a consistent playground state before tests run, supporting the PR objective to make tests more resilient to Strapi UI changes. The step creates the .tmp directory and runs the setup script, which should deterministically initialize the test environment.


61-65: Fix template syntax spacing and verify Cypress recording configuration.

The template syntax spacing inconsistency is confirmed—line 63 uses ${{ secrets.CYPRESS_RECORD_KEY }} while line 65 uses ${{secrets.GITHUB_TOKEN}}. Additionally, the projectId: 'vu3oo5' has been confirmed in cypress.config.js.

To complete the Cypress recording setup:

  • Ensure CYPRESS_RECORD_KEY is configured in your repository secrets
  • Apply the spacing fix to line 65 for consistency:
  env:
    CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
    # Allows Cypress to detect new build vs re-run build
-   GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
+   GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
cypress/e2e/features.cy.js (1)

154-160: Clarify test isolation concern.

The test assumes checkboxes are initially unchecked, but if previous tests don't clean up properly or tests run out of order, this will fail. The cy.fail() call prevents accidental deletion but doesn't help with test isolation.

Consider either:

  1. Adding a test setup/teardown to ensure consistent initial state
  2. Modifying the test to handle both checked and unchecked states gracefully
  3. Adding a .skip() or guard if the checkbox is already in the desired state

Verify whether test ordering and cleanup are guaranteed in your CI environment.

Comment on lines +35 to +44
const checkCollectionContent = ({ rowNb, contains }) => {
const row = `table[role='grid'] tbody tr:nth-child(${rowNb})`
contains.map(value =>
cy
.get(row, {
timeout: 10000,
})
.contains(value, { timeout: 10000 }),
)
}
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 .map() usage in Cypress command chain.

The .map() method returns a JavaScript array, but it doesn't properly integrate with Cypress command chaining. Each cy.get() call executes independently without proper synchronization.

Apply this diff to use .forEach() instead:

   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 }),
     )
   }
🤖 Prompt for AI Agents
In cypress/e2e/features.cy.js around lines 35 to 44, the function uses
contains.map(...) which returns a JS array and misuses Cypress command chaining;
replace the .map() with .forEach() so each value is iterated and a
cy.get(...).contains(...) is invoked for side effects, i.e., change
contains.map(...) to contains.forEach(...), keeping the existing cy.get/contains
calls and their timeouts so Cypress executes them in sequence for each value.

})

// This test assumes that the collections are indexed by previous tests
it('displays the number of inxed documents for each collection', () => {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in test description.

"inxed" should be "indexed".

Apply this diff:

-    it('displays the number of inxed documents for each collection', () => {
+    it('displays the number of indexed documents for each collection', () => {
📝 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('displays the number of inxed documents for each collection', () => {
it('displays the number of indexed documents for each collection', () => {
🤖 Prompt for AI Agents
In cypress/e2e/features.cy.js around line 171 the test description contains a
typo "inxed" instead of "indexed"; update the string in the it(...) call to read
"displays the number of indexed documents for each collection" so the test
message is correct.

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.

2 participants