@verdaccio/e2e-ui 2.4.1 → 2.4.3

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.
Files changed (41) hide show
  1. package/README.md +18 -12
  2. package/build/cjs/commands/index.cjs.map +1 -1
  3. package/build/cjs/features.cjs +1 -0
  4. package/build/cjs/features.cjs.map +1 -1
  5. package/build/cjs/index.cjs +28 -3
  6. package/build/cjs/index.cjs.map +1 -1
  7. package/build/cjs/tasks/publish.cjs.map +1 -1
  8. package/build/cjs/testIds.cjs +1 -0
  9. package/build/cjs/testIds.cjs.map +1 -1
  10. package/build/cjs/tests/change-password.cjs.map +1 -1
  11. package/build/cjs/tests/home.cjs.map +1 -1
  12. package/build/cjs/tests/layout.cjs.map +1 -1
  13. package/build/cjs/tests/publish.cjs +64 -24
  14. package/build/cjs/tests/publish.cjs.map +1 -1
  15. package/build/cjs/tests/search.cjs +0 -7
  16. package/build/cjs/tests/search.cjs.map +1 -1
  17. package/build/cjs/tests/settings.cjs.map +1 -1
  18. package/build/cjs/tests/signin.cjs.map +1 -1
  19. package/build/commands/index.d.ts +0 -2
  20. package/build/esm/commands/index.js.map +1 -1
  21. package/build/esm/features.js +1 -0
  22. package/build/esm/features.js.map +1 -1
  23. package/build/esm/index.js +28 -3
  24. package/build/esm/index.js.map +1 -1
  25. package/build/esm/tasks/publish.js.map +1 -1
  26. package/build/esm/testIds.js +1 -0
  27. package/build/esm/testIds.js.map +1 -1
  28. package/build/esm/tests/change-password.js.map +1 -1
  29. package/build/esm/tests/home.js.map +1 -1
  30. package/build/esm/tests/layout.js.map +1 -1
  31. package/build/esm/tests/publish.js +64 -24
  32. package/build/esm/tests/publish.js.map +1 -1
  33. package/build/esm/tests/search.js +0 -7
  34. package/build/esm/tests/search.js.map +1 -1
  35. package/build/esm/tests/settings.js.map +1 -1
  36. package/build/esm/tests/signin.js.map +1 -1
  37. package/build/features.d.ts +16 -6
  38. package/build/index.d.ts +1 -2
  39. package/build/testIds.d.ts +2 -0
  40. package/build/types.d.ts +0 -3
  41. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","names":[],"sources":["../../../src/tests/settings.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\n\nimport { maybeIt } from '../features';\nimport { RegistryConfig } from '../types';\n\n/**\n * Tests for the header Settings dialog and the Translations (language)\n * picker inside it.\n *\n * Requires `web.showSettings: true` in the registry config — the gear\n * icon only renders when the flag is on (see HeaderRight in Verdaccio's\n * ui-components package).\n *\n * Selector strategy: the language cards in LanguageSwitch do NOT have\n * data-testids, so this suite leans on:\n * - MUI's ARIA roles: `[role=\"dialog\"]`, `[role=\"tab\"]`\n * - The stable English labels \"Configuration\", \"Translations\",\n * \"English\", \"German\", and the language description text, which are\n * bundled into the ui-theme via `@verdaccio/ui-i18n`\n * - The settings trigger's testid from Verdaccio's HeaderToolTipIcon:\n * `header--tooltip-settings`\n */\nexport function settingsTests(config: RegistryConfig) {\n const { header } = config.testIds;\n const { features } = config;\n\n describe('settings & language', () => {\n beforeEach(() => {\n cy.visit(config.registryUrl);\n cy.get('body').should('be.visible');\n });\n\n it('should open the settings dialog from the header', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n // Dialog title key: dialog.settings.title → \"Configuration\" in EN.\n cy.contains('[role=\"dialog\"]', 'Configuration').should('be.visible');\n });\n\n it('should show both Package Managers and Translations tabs', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.get('[role=\"dialog\"] [role=\"tab\"]').should('have.length', 2);\n cy.contains('[role=\"dialog\"] [role=\"tab\"]', 'Translations').should(\n 'be.visible'\n );\n });\n\n it('should switch to the Translations tab and list languages', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.contains('[role=\"dialog\"] [role=\"tab\"]', 'Translations').click();\n // language.description text is bundled in EN — good sentinel.\n cy.contains(\n '[role=\"dialog\"]',\n /this is the configuration details for the language/i\n ).should('be.visible');\n // Sanity-check a couple of language cards are rendered.\n cy.contains('[role=\"dialog\"]', 'English').should('be.visible');\n cy.contains('[role=\"dialog\"]', 'German').should('be.visible');\n });\n\n maybeIt(features.settings.languageSwitcher)(\n 'should change the UI language when a language card is clicked',\n () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.contains('[role=\"dialog\"] [role=\"tab\"]', 'Translations').click();\n\n // Before switching, capture the current dialog title so we can\n // assert it changes. This avoids hard-coding the German string.\n cy.contains('[role=\"dialog\"]', 'Configuration')\n .invoke('text')\n .then((englishTitle) => {\n // Click the German card. We scope to the dialog + the MUI\n // card class to avoid matching other \"German\" text.\n cy.contains('[role=\"dialog\"] .MuiCard-root', 'German').click({\n force: true,\n });\n\n // The English tab label \"Translations\" must no longer be\n // present anywhere in the dialog — strongest proof the\n // language actually switched.\n cy.contains('[role=\"dialog\"] [role=\"tab\"]', 'Translations').should(\n 'not.exist'\n );\n // And the original dialog title text should also be gone.\n cy.contains('[role=\"dialog\"]', englishTitle).should('not.exist');\n });\n }\n );\n\n it('should close the settings dialog with Escape', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.get('body').type('{esc}');\n cy.get('[role=\"dialog\"]').should('not.exist');\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,cAAc,QAAwB;CACpD,MAAM,EAAE,WAAW,OAAO;CAC1B,MAAM,EAAE,aAAa;AAErB,UAAS,6BAA6B;AACpC,mBAAiB;AACf,MAAG,MAAM,OAAO,YAAY;AAC5B,MAAG,IAAI,OAAO,CAAC,OAAO,aAAa;IACnC;AAEF,KAAG,yDAAyD;AAC1D,MAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO;AAC9C,MAAG,IAAI,oBAAkB,CAAC,OAAO,aAAa;AAE9C,MAAG,SAAS,qBAAmB,gBAAgB,CAAC,OAAO,aAAa;IACpE;AAEF,KAAG,iEAAiE;AAClE,MAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO;AAC9C,MAAG,IAAI,oBAAkB,CAAC,OAAO,aAAa;AAC9C,MAAG,IAAI,mCAA+B,CAAC,OAAO,eAAe,EAAE;AAC/D,MAAG,SAAS,oCAAgC,eAAe,CAAC,OAC1D,aACD;IACD;AAEF,KAAG,kEAAkE;AACnE,MAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO;AAC9C,MAAG,IAAI,oBAAkB,CAAC,OAAO,aAAa;AAC9C,MAAG,SAAS,oCAAgC,eAAe,CAAC,OAAO;AAEnE,MAAG,SACD,qBACA,sDACD,CAAC,OAAO,aAAa;AAEtB,MAAG,SAAS,qBAAmB,UAAU,CAAC,OAAO,aAAa;AAC9D,MAAG,SAAS,qBAAmB,SAAS,CAAC,OAAO,aAAa;IAC7D;AAEF,UAAQ,SAAS,SAAS,iBAAiB,CACzC,uEACM;AACN,MAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO;AAC9C,MAAG,IAAI,oBAAkB,CAAC,OAAO,aAAa;AAC9C,MAAG,SAAS,oCAAgC,eAAe,CAAC,OAAO;AAInE,MAAG,SAAS,qBAAmB,gBAAgB,CAC5C,OAAO,OAAO,CACd,MAAM,iBAAiB;AAGtB,OAAG,SAAS,mCAAiC,SAAS,CAAC,MAAM,EAC3D,OAAO,MACR,CAAC;AAKF,OAAG,SAAS,oCAAgC,eAAe,CAAC,OAC1D,YACD;AAED,OAAG,SAAS,qBAAmB,aAAa,CAAC,OAAO,YAAY;KAChE;IAEL;AAED,KAAG,sDAAsD;AACvD,MAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO;AAC9C,MAAG,IAAI,oBAAkB,CAAC,OAAO,aAAa;AAC9C,MAAG,IAAI,OAAO,CAAC,KAAK,QAAQ;AAC5B,MAAG,IAAI,oBAAkB,CAAC,OAAO,YAAY;IAC7C;GACF"}
1
+ {"version":3,"file":"settings.js","names":[],"sources":["../../../src/tests/settings.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\nimport { maybeIt } from '../features';\nimport { RegistryConfig } from '../types';\n\n/**\n * Tests for the header Settings dialog and the Translations (language)\n * picker inside it.\n *\n * Requires `web.showSettings: true` in the registry config — the gear\n * icon only renders when the flag is on (see HeaderRight in Verdaccio's\n * ui-components package).\n *\n * Selector strategy: the language cards in LanguageSwitch do NOT have\n * data-testids, so this suite leans on:\n * - MUI's ARIA roles: `[role=\"dialog\"]`, `[role=\"tab\"]`\n * - The stable English labels \"Configuration\", \"Translations\",\n * \"English\", \"German\", and the language description text, which are\n * bundled into the ui-theme via `@verdaccio/ui-i18n`\n * - The settings trigger's testid from Verdaccio's HeaderToolTipIcon:\n * `header--tooltip-settings`\n */\nexport function settingsTests(config: RegistryConfig) {\n const { header } = config.testIds;\n const { features } = config;\n\n describe('settings & language', () => {\n beforeEach(() => {\n cy.visit(config.registryUrl);\n cy.get('body').should('be.visible');\n });\n\n it('should open the settings dialog from the header', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n // Dialog title key: dialog.settings.title → \"Configuration\" in EN.\n cy.contains('[role=\"dialog\"]', 'Configuration').should('be.visible');\n });\n\n it('should show both Package Managers and Translations tabs', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.get('[role=\"dialog\"] [role=\"tab\"]').should('have.length', 2);\n cy.contains('[role=\"dialog\"] [role=\"tab\"]', 'Translations').should('be.visible');\n });\n\n it('should switch to the Translations tab and list languages', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.contains('[role=\"dialog\"] [role=\"tab\"]', 'Translations').click();\n // language.description text is bundled in EN — good sentinel.\n cy.contains('[role=\"dialog\"]', /this is the configuration details for the language/i).should(\n 'be.visible'\n );\n // Sanity-check a couple of language cards are rendered.\n cy.contains('[role=\"dialog\"]', 'English').should('be.visible');\n cy.contains('[role=\"dialog\"]', 'German').should('be.visible');\n });\n\n maybeIt(features.settings.languageSwitcher)(\n 'should change the UI language when a language card is clicked',\n () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.contains('[role=\"dialog\"] [role=\"tab\"]', 'Translations').click();\n\n // Before switching, capture the current dialog title so we can\n // assert it changes. This avoids hard-coding the German string.\n cy.contains('[role=\"dialog\"]', 'Configuration')\n .invoke('text')\n .then((englishTitle) => {\n // Click the German card. We scope to the dialog + the MUI\n // card class to avoid matching other \"German\" text.\n cy.contains('[role=\"dialog\"] .MuiCard-root', 'German').click({\n force: true,\n });\n\n // The English tab label \"Translations\" must no longer be\n // present anywhere in the dialog — strongest proof the\n // language actually switched.\n cy.contains('[role=\"dialog\"] [role=\"tab\"]', 'Translations').should('not.exist');\n // And the original dialog title text should also be gone.\n cy.contains('[role=\"dialog\"]', englishTitle).should('not.exist');\n });\n }\n );\n\n it('should close the settings dialog with Escape', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.get('body').type('{esc}');\n cy.get('[role=\"dialog\"]').should('not.exist');\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,cAAc,QAAwB;CACpD,MAAM,EAAE,WAAW,OAAO;CAC1B,MAAM,EAAE,aAAa;CAErB,SAAS,6BAA6B;EACpC,iBAAiB;GACf,GAAG,MAAM,OAAO,WAAW;GAC3B,GAAG,IAAI,MAAM,CAAC,CAAC,OAAO,YAAY;EACpC,CAAC;EAED,GAAG,yDAAyD;GAC1D,GAAG,YAAY,OAAO,eAAe,CAAC,CAAC,MAAM;GAC7C,GAAG,IAAI,mBAAiB,CAAC,CAAC,OAAO,YAAY;GAE7C,GAAG,SAAS,qBAAmB,eAAe,CAAC,CAAC,OAAO,YAAY;EACrE,CAAC;EAED,GAAG,iEAAiE;GAClE,GAAG,YAAY,OAAO,eAAe,CAAC,CAAC,MAAM;GAC7C,GAAG,IAAI,mBAAiB,CAAC,CAAC,OAAO,YAAY;GAC7C,GAAG,IAAI,kCAA8B,CAAC,CAAC,OAAO,eAAe,CAAC;GAC9D,GAAG,SAAS,oCAAgC,cAAc,CAAC,CAAC,OAAO,YAAY;EACjF,CAAC;EAED,GAAG,kEAAkE;GACnE,GAAG,YAAY,OAAO,eAAe,CAAC,CAAC,MAAM;GAC7C,GAAG,IAAI,mBAAiB,CAAC,CAAC,OAAO,YAAY;GAC7C,GAAG,SAAS,oCAAgC,cAAc,CAAC,CAAC,MAAM;GAElE,GAAG,SAAS,qBAAmB,qDAAqD,CAAC,CAAC,OACpF,YACF;GAEA,GAAG,SAAS,qBAAmB,SAAS,CAAC,CAAC,OAAO,YAAY;GAC7D,GAAG,SAAS,qBAAmB,QAAQ,CAAC,CAAC,OAAO,YAAY;EAC9D,CAAC;EAED,QAAQ,SAAS,SAAS,gBAAgB,CAAC,CACzC,uEACM;GACJ,GAAG,YAAY,OAAO,eAAe,CAAC,CAAC,MAAM;GAC7C,GAAG,IAAI,mBAAiB,CAAC,CAAC,OAAO,YAAY;GAC7C,GAAG,SAAS,oCAAgC,cAAc,CAAC,CAAC,MAAM;GAIlE,GAAG,SAAS,qBAAmB,eAAe,CAAC,CAC5C,OAAO,MAAM,CAAC,CACd,MAAM,iBAAiB;IAGtB,GAAG,SAAS,mCAAiC,QAAQ,CAAC,CAAC,MAAM,EAC3D,OAAO,KACT,CAAC;IAKD,GAAG,SAAS,oCAAgC,cAAc,CAAC,CAAC,OAAO,WAAW;IAE9E,GAAG,SAAS,qBAAmB,YAAY,CAAC,CAAC,OAAO,WAAW;GACjE,CAAC;EACL,CACF;EAEA,GAAG,sDAAsD;GACvD,GAAG,YAAY,OAAO,eAAe,CAAC,CAAC,MAAM;GAC7C,GAAG,IAAI,mBAAiB,CAAC,CAAC,OAAO,YAAY;GAC7C,GAAG,IAAI,MAAM,CAAC,CAAC,KAAK,OAAO;GAC3B,GAAG,IAAI,mBAAiB,CAAC,CAAC,OAAO,WAAW;EAC9C,CAAC;CACH,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"signin.js","names":[],"sources":["../../../src/tests/signin.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\n\nimport { maybeIt } from '../features';\nimport { RegistryConfig } from '../types';\n\nexport function signinTests(config: RegistryConfig) {\n const { header, login } = config.testIds;\n const { loginDialog } = config.selectors;\n const { features } = config;\n\n describe('sign in / sign out', () => {\n beforeEach(() => {\n cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');\n cy.visit(config.registryUrl);\n });\n\n it('should login user', () => {\n cy.login(config.credentials.user, config.credentials.password, {\n loginButton: header.loginButton,\n ...loginDialog,\n });\n cy.wait('@sign');\n cy.getByTestId(header.logInDialogIcon).click();\n cy.wait(100);\n cy.getByTestId(header.greetingsLabel).contains(config.credentials.user);\n });\n\n it('should logout a user', () => {\n cy.login(config.credentials.user, config.credentials.password, {\n loginButton: header.loginButton,\n ...loginDialog,\n });\n cy.wait('@sign');\n cy.getByTestId(header.logInDialogIcon).click();\n cy.wait(100);\n cy.getByTestId(header.logOutDialogIcon).click();\n cy.wait(200);\n cy.getByTestId(header.loginButton).contains('Login');\n });\n\n // ── Validation tests ───────────────────────────────────────────\n // The login form uses a yup schema (username >= 2 chars + URL-safe,\n // password >= 2 chars) and the submit button is `disabled={!isValid}`.\n // Server rejection renders an error banner with `data-testid=\"error\"`\n // inside `LoginDialogFormError`. Gated on\n // `features.signin.validationTests` so builds with a different\n // schema or error string can skip without forking.\n\n maybeIt(features.signin.validationTests)(\n 'should disable the submit button while the form is empty',\n () => {\n cy.getByTestId(header.loginButton).click();\n cy.getByTestId(login.dialog).should('be.visible');\n // Neither field filled → schema invalid → button disabled.\n cy.get(loginDialog.submitButton).should('be.disabled');\n }\n );\n\n maybeIt(features.signin.validationTests)(\n 'should keep the submit button disabled when only the username is filled',\n () => {\n cy.getByTestId(header.loginButton).click();\n cy.getByTestId(login.dialog).should('be.visible');\n cy.get(loginDialog.usernameInput).type(config.credentials.user);\n // Password still empty → schema still invalid.\n cy.get(loginDialog.submitButton).should('be.disabled');\n }\n );\n\n maybeIt(features.signin.validationTests)(\n 'should show an error banner on invalid credentials',\n () => {\n cy.login(config.credentials.user, 'definitely-wrong-password-xyz', {\n loginButton: header.loginButton,\n ...loginDialog,\n });\n // The request goes through (valid schema), the server rejects it.\n cy.wait('@sign').its('response.statusCode').should('not.eq', 200);\n // The form sets errors.root which renders via LoginDialogFormError\n // with a fixed English string (\"Invalid username or password\").\n cy.getByTestId(login.error, { timeout: 5000 }).should('be.visible');\n cy.getByTestId(login.error).should(\n 'contain.text',\n 'Invalid username or password'\n );\n // Dialog should still be open so the user can retry.\n cy.getByTestId(login.dialog).should('be.visible');\n }\n );\n });\n}\n"],"mappings":";;AAKA,SAAgB,YAAY,QAAwB;CAClD,MAAM,EAAE,QAAQ,UAAU,OAAO;CACjC,MAAM,EAAE,gBAAgB,OAAO;CAC/B,MAAM,EAAE,aAAa;AAErB,UAAS,4BAA4B;AACnC,mBAAiB;AACf,MAAG,UAAU,QAAQ,yBAAyB,CAAC,GAAG,OAAO;AACzD,MAAG,MAAM,OAAO,YAAY;IAC5B;AAEF,KAAG,2BAA2B;AAC5B,MAAG,MAAM,OAAO,YAAY,MAAM,OAAO,YAAY,UAAU;IAC7D,aAAa,OAAO;IACpB,GAAG;IACJ,CAAC;AACF,MAAG,KAAK,QAAQ;AAChB,MAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO;AAC9C,MAAG,KAAK,IAAI;AACZ,MAAG,YAAY,OAAO,eAAe,CAAC,SAAS,OAAO,YAAY,KAAK;IACvE;AAEF,KAAG,8BAA8B;AAC/B,MAAG,MAAM,OAAO,YAAY,MAAM,OAAO,YAAY,UAAU;IAC7D,aAAa,OAAO;IACpB,GAAG;IACJ,CAAC;AACF,MAAG,KAAK,QAAQ;AAChB,MAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO;AAC9C,MAAG,KAAK,IAAI;AACZ,MAAG,YAAY,OAAO,iBAAiB,CAAC,OAAO;AAC/C,MAAG,KAAK,IAAI;AACZ,MAAG,YAAY,OAAO,YAAY,CAAC,SAAS,QAAQ;IACpD;AAUF,UAAQ,SAAS,OAAO,gBAAgB,CACtC,kEACM;AACJ,MAAG,YAAY,OAAO,YAAY,CAAC,OAAO;AAC1C,MAAG,YAAY,MAAM,OAAO,CAAC,OAAO,aAAa;AAEjD,MAAG,IAAI,YAAY,aAAa,CAAC,OAAO,cAAc;IAEzD;AAED,UAAQ,SAAS,OAAO,gBAAgB,CACtC,iFACM;AACJ,MAAG,YAAY,OAAO,YAAY,CAAC,OAAO;AAC1C,MAAG,YAAY,MAAM,OAAO,CAAC,OAAO,aAAa;AACjD,MAAG,IAAI,YAAY,cAAc,CAAC,KAAK,OAAO,YAAY,KAAK;AAE/D,MAAG,IAAI,YAAY,aAAa,CAAC,OAAO,cAAc;IAEzD;AAED,UAAQ,SAAS,OAAO,gBAAgB,CACtC,4DACM;AACJ,MAAG,MAAM,OAAO,YAAY,MAAM,iCAAiC;IACjE,aAAa,OAAO;IACpB,GAAG;IACJ,CAAC;AAEF,MAAG,KAAK,QAAQ,CAAC,IAAI,sBAAsB,CAAC,OAAO,UAAU,IAAI;AAGjE,MAAG,YAAY,MAAM,OAAO,EAAE,SAAS,KAAM,CAAC,CAAC,OAAO,aAAa;AACnE,MAAG,YAAY,MAAM,MAAM,CAAC,OAC1B,gBACA,+BACD;AAED,MAAG,YAAY,MAAM,OAAO,CAAC,OAAO,aAAa;IAEpD;GACD"}
1
+ {"version":3,"file":"signin.js","names":[],"sources":["../../../src/tests/signin.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\nimport { maybeIt } from '../features';\nimport { RegistryConfig } from '../types';\n\nexport function signinTests(config: RegistryConfig) {\n const { header, login } = config.testIds;\n const { loginDialog } = config.selectors;\n const { features } = config;\n\n describe('sign in / sign out', () => {\n beforeEach(() => {\n cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');\n cy.visit(config.registryUrl);\n });\n\n it('should login user', () => {\n cy.login(config.credentials.user, config.credentials.password, {\n loginButton: header.loginButton,\n ...loginDialog,\n });\n cy.wait('@sign');\n cy.getByTestId(header.logInDialogIcon).click();\n cy.wait(100);\n cy.getByTestId(header.greetingsLabel).contains(config.credentials.user);\n });\n\n it('should logout a user', () => {\n cy.login(config.credentials.user, config.credentials.password, {\n loginButton: header.loginButton,\n ...loginDialog,\n });\n cy.wait('@sign');\n cy.getByTestId(header.logInDialogIcon).click();\n cy.wait(100);\n cy.getByTestId(header.logOutDialogIcon).click();\n cy.wait(200);\n cy.getByTestId(header.loginButton).contains('Login');\n });\n\n // ── Validation tests ───────────────────────────────────────────\n // The login form uses a yup schema (username >= 2 chars + URL-safe,\n // password >= 2 chars) and the submit button is `disabled={!isValid}`.\n // Server rejection renders an error banner with `data-testid=\"error\"`\n // inside `LoginDialogFormError`. Gated on\n // `features.signin.validationTests` so builds with a different\n // schema or error string can skip without forking.\n\n maybeIt(features.signin.validationTests)(\n 'should disable the submit button while the form is empty',\n () => {\n cy.getByTestId(header.loginButton).click();\n cy.getByTestId(login.dialog).should('be.visible');\n // Neither field filled → schema invalid → button disabled.\n cy.get(loginDialog.submitButton).should('be.disabled');\n }\n );\n\n maybeIt(features.signin.validationTests)(\n 'should keep the submit button disabled when only the username is filled',\n () => {\n cy.getByTestId(header.loginButton).click();\n cy.getByTestId(login.dialog).should('be.visible');\n cy.get(loginDialog.usernameInput).type(config.credentials.user);\n // Password still empty → schema still invalid.\n cy.get(loginDialog.submitButton).should('be.disabled');\n }\n );\n\n maybeIt(features.signin.validationTests)(\n 'should show an error banner on invalid credentials',\n () => {\n cy.login(config.credentials.user, 'definitely-wrong-password-xyz', {\n loginButton: header.loginButton,\n ...loginDialog,\n });\n // The request goes through (valid schema), the server rejects it.\n cy.wait('@sign').its('response.statusCode').should('not.eq', 200);\n // The form sets errors.root which renders via LoginDialogFormError\n // with a fixed English string (\"Invalid username or password\").\n cy.getByTestId(login.error, { timeout: 5000 }).should('be.visible');\n cy.getByTestId(login.error).should('contain.text', 'Invalid username or password');\n // Dialog should still be open so the user can retry.\n cy.getByTestId(login.dialog).should('be.visible');\n }\n );\n });\n}\n"],"mappings":";;AAIA,SAAgB,YAAY,QAAwB;CAClD,MAAM,EAAE,QAAQ,UAAU,OAAO;CACjC,MAAM,EAAE,gBAAgB,OAAO;CAC/B,MAAM,EAAE,aAAa;CAErB,SAAS,4BAA4B;EACnC,iBAAiB;GACf,GAAG,UAAU,QAAQ,wBAAwB,CAAC,CAAC,GAAG,MAAM;GACxD,GAAG,MAAM,OAAO,WAAW;EAC7B,CAAC;EAED,GAAG,2BAA2B;GAC5B,GAAG,MAAM,OAAO,YAAY,MAAM,OAAO,YAAY,UAAU;IAC7D,aAAa,OAAO;IACpB,GAAG;GACL,CAAC;GACD,GAAG,KAAK,OAAO;GACf,GAAG,YAAY,OAAO,eAAe,CAAC,CAAC,MAAM;GAC7C,GAAG,KAAK,GAAG;GACX,GAAG,YAAY,OAAO,cAAc,CAAC,CAAC,SAAS,OAAO,YAAY,IAAI;EACxE,CAAC;EAED,GAAG,8BAA8B;GAC/B,GAAG,MAAM,OAAO,YAAY,MAAM,OAAO,YAAY,UAAU;IAC7D,aAAa,OAAO;IACpB,GAAG;GACL,CAAC;GACD,GAAG,KAAK,OAAO;GACf,GAAG,YAAY,OAAO,eAAe,CAAC,CAAC,MAAM;GAC7C,GAAG,KAAK,GAAG;GACX,GAAG,YAAY,OAAO,gBAAgB,CAAC,CAAC,MAAM;GAC9C,GAAG,KAAK,GAAG;GACX,GAAG,YAAY,OAAO,WAAW,CAAC,CAAC,SAAS,OAAO;EACrD,CAAC;EAUD,QAAQ,SAAS,OAAO,eAAe,CAAC,CACtC,kEACM;GACJ,GAAG,YAAY,OAAO,WAAW,CAAC,CAAC,MAAM;GACzC,GAAG,YAAY,MAAM,MAAM,CAAC,CAAC,OAAO,YAAY;GAEhD,GAAG,IAAI,YAAY,YAAY,CAAC,CAAC,OAAO,aAAa;EACvD,CACF;EAEA,QAAQ,SAAS,OAAO,eAAe,CAAC,CACtC,iFACM;GACJ,GAAG,YAAY,OAAO,WAAW,CAAC,CAAC,MAAM;GACzC,GAAG,YAAY,MAAM,MAAM,CAAC,CAAC,OAAO,YAAY;GAChD,GAAG,IAAI,YAAY,aAAa,CAAC,CAAC,KAAK,OAAO,YAAY,IAAI;GAE9D,GAAG,IAAI,YAAY,YAAY,CAAC,CAAC,OAAO,aAAa;EACvD,CACF;EAEA,QAAQ,SAAS,OAAO,eAAe,CAAC,CACtC,4DACM;GACJ,GAAG,MAAM,OAAO,YAAY,MAAM,iCAAiC;IACjE,aAAa,OAAO;IACpB,GAAG;GACL,CAAC;GAED,GAAG,KAAK,OAAO,CAAC,CAAC,IAAI,qBAAqB,CAAC,CAAC,OAAO,UAAU,GAAG;GAGhE,GAAG,YAAY,MAAM,OAAO,EAAE,SAAS,IAAK,CAAC,CAAC,CAAC,OAAO,YAAY;GAClE,GAAG,YAAY,MAAM,KAAK,CAAC,CAAC,OAAO,gBAAgB,8BAA8B;GAEjF,GAAG,YAAY,MAAM,MAAM,CAAC,CAAC,OAAO,YAAY;EAClD,CACF;CACF,CAAC;AACH"}
@@ -1,5 +1,4 @@
1
- /// <reference types="cypress" />
2
- /// <reference types="cypress" />
1
+ import type { DeepPartial } from './testIds';
3
2
  /**
4
3
  * Per-test feature flags for enabling/disabling individual test cases
5
4
  * in the e2e-ui suites.
@@ -11,9 +10,11 @@
11
10
  * branch, consumers can disable individual tests via
12
11
  * `createRegistryConfig({ features: { … } })`.
13
12
  *
14
- * Every flag defaults to `true` (test runs). Override to `false` to
15
- * convert the test into a Mocha `it.skip` call the suite still
16
- * reports the test but marks it as pending.
13
+ * Most flags default to `true` (test runs). Compatibility probes for
14
+ * unreleased server behavior may default to `false`; enable them in
15
+ * consumers that run against a Verdaccio build with the feature.
16
+ * A disabled flag converts the test into a Mocha `it.skip` call — the
17
+ * suite still reports the test but marks it as pending.
17
18
  */
18
19
  export interface Features {
19
20
  search: {
@@ -70,6 +71,16 @@ export interface Features {
70
71
  * the published package manifest having a valid `dist.tarball`.
71
72
  */
72
73
  downloadTarball: boolean;
74
+ /**
75
+ * Whether to run the private package tarball download tests from
76
+ * the Web UI package list and sidebar.
77
+ *
78
+ * Enable only on Verdaccio builds that accept Web UI bearer tokens
79
+ * on protected npm package/tarball routes. Verdaccio 6.x currently
80
+ * returns HTTP 401 for this flow; the regression is tracked in
81
+ * verdaccio/verdaccio#5765.
82
+ */
83
+ privateDownloadTarball: boolean;
73
84
  /**
74
85
  * Whether to run the "raw viewer dialog opens + closes" test.
75
86
  * Depends on `web.showRaw` (defaults to true).
@@ -114,7 +125,6 @@ export interface Features {
114
125
  }
115
126
  /** Defaults: all flags on. */
116
127
  export declare const DEFAULT_FEATURES: Features;
117
- import type { DeepPartial } from './testIds';
118
128
  /**
119
129
  * Merge user overrides into the default feature flags. Per-section,
120
130
  * one level deep — matching the style of `mergeTestIds`.
package/build/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- /// <reference types="cypress" />
2
1
  import { RegistryConfig, VerdaccioUiOptions } from './types';
3
- export type { RegistryConfig, RegistryTaskResult, VerdaccioUiOptions, } from './types';
2
+ export type { RegistryConfig, RegistryTaskResult, VerdaccioUiOptions } from './types';
4
3
  export type { PublishPackageInput, PublishPackageTaskInput, PublishPackageResult, UnpublishPackageInput, UnpublishPackageResult, } from './tasks';
5
4
  export type { DeepPartial, Selectors, TestIds } from './testIds';
6
5
  export type { Features } from './features';
@@ -107,6 +107,8 @@ export interface TestIds {
107
107
  noUplinks: string;
108
108
  /** Action-bar tarball download FAB. */
109
109
  downloadTarballBtn: string;
110
+ /** Package list tarball download button. */
111
+ downloadTarball: string;
110
112
  /** Action-bar "view raw manifest" FAB. */
111
113
  rawBtn: string;
112
114
  /** Full-screen dialog that opens when `rawBtn` is clicked. */
package/build/types.d.ts CHANGED
@@ -1,6 +1,3 @@
1
- /// <reference types="cypress" />
2
- /// <reference types="cypress" />
3
- /// <reference types="cypress" />
4
1
  import type { Features } from './features';
5
2
  import type { PublishPackageResult, PublishPackageTaskInput, UnpublishPackageInput, UnpublishPackageResult } from './tasks';
6
3
  import type { DeepPartial, Selectors, TestIds } from './testIds';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/e2e-ui",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Cypress plugin for Verdaccio UI e2e tests — run against any registry",
@@ -31,9 +31,9 @@
31
31
  "cypress": ">=12.0.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@types/node": "20.19.0",
35
- "cypress": "15.12.0",
36
- "vite": "8.0.1"
34
+ "@types/node": "26.2.0",
35
+ "cypress": "15.20.1",
36
+ "vite": "8.2.1"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.build.json",