@verdaccio/e2e-ui 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/build/cjs/features.cjs +34 -0
  2. package/build/cjs/features.cjs.map +1 -1
  3. package/build/cjs/index.cjs +13 -0
  4. package/build/cjs/index.cjs.map +1 -1
  5. package/build/cjs/testIds.cjs +16 -1
  6. package/build/cjs/testIds.cjs.map +1 -1
  7. package/build/cjs/tests/detail.cjs +73 -0
  8. package/build/cjs/tests/detail.cjs.map +1 -0
  9. package/build/cjs/tests/failure-modes.cjs +75 -0
  10. package/build/cjs/tests/failure-modes.cjs.map +1 -0
  11. package/build/cjs/tests/i18n-keys.cjs +74 -0
  12. package/build/cjs/tests/i18n-keys.cjs.map +1 -0
  13. package/build/cjs/tests/index.cjs +22 -0
  14. package/build/cjs/tests/signup.cjs +56 -0
  15. package/build/cjs/tests/signup.cjs.map +1 -0
  16. package/build/esm/features.js +34 -0
  17. package/build/esm/features.js.map +1 -1
  18. package/build/esm/index.js +10 -1
  19. package/build/esm/index.js.map +1 -1
  20. package/build/esm/testIds.js +16 -1
  21. package/build/esm/testIds.js.map +1 -1
  22. package/build/esm/tests/detail.js +73 -0
  23. package/build/esm/tests/detail.js.map +1 -0
  24. package/build/esm/tests/failure-modes.js +75 -0
  25. package/build/esm/tests/failure-modes.js.map +1 -0
  26. package/build/esm/tests/i18n-keys.js +74 -0
  27. package/build/esm/tests/i18n-keys.js.map +1 -0
  28. package/build/esm/tests/index.js +12 -0
  29. package/build/esm/tests/signup.js +56 -0
  30. package/build/esm/tests/signup.js.map +1 -0
  31. package/build/features.d.ts +79 -0
  32. package/build/index.d.ts +1 -1
  33. package/build/testIds.d.ts +18 -0
  34. package/build/tests/detail.d.ts +12 -0
  35. package/build/tests/failure-modes.d.ts +11 -0
  36. package/build/tests/i18n-keys.d.ts +2 -0
  37. package/build/tests/index.d.ts +4 -0
  38. package/build/tests/signup.d.ts +12 -0
  39. package/package.json +1 -1
@@ -14,6 +14,24 @@ var DEFAULT_FEATURES = {
14
14
  privateDownloadTarball: false,
15
15
  rawViewer: true
16
16
  },
17
+ detail: {
18
+ versionPinned: true,
19
+ scopedWireFormat: true,
20
+ versionNotFound: false
21
+ },
22
+ failureModes: {
23
+ homeServerError: true,
24
+ homeNetworkError: false,
25
+ detailErrorState: false
26
+ },
27
+ i18n: {
28
+ noRawKeysCorePages: true,
29
+ noRawKeysSecurityPages: false
30
+ },
31
+ signup: {
32
+ happyPath: false,
33
+ validation: false
34
+ },
17
35
  changePassword: {
18
36
  happyPath: true,
19
37
  validation: true,
@@ -51,6 +69,22 @@ function mergeFeatures(defaults, overrides) {
51
69
  ...defaults.publish,
52
70
  ...overrides.publish
53
71
  },
72
+ detail: {
73
+ ...defaults.detail,
74
+ ...overrides.detail
75
+ },
76
+ failureModes: {
77
+ ...defaults.failureModes,
78
+ ...overrides.failureModes
79
+ },
80
+ i18n: {
81
+ ...defaults.i18n,
82
+ ...overrides.i18n
83
+ },
84
+ signup: {
85
+ ...defaults.signup,
86
+ ...overrides.signup
87
+ },
54
88
  changePassword: {
55
89
  ...defaults.changePassword,
56
90
  ...overrides.changePassword
@@ -1 +1 @@
1
- {"version":3,"file":"features.cjs","names":[],"sources":["../../src/features.ts"],"sourcesContent":["import type { DeepPartial } from './testIds';\n\n/**\n * Per-test feature flags for enabling/disabling individual test cases\n * in the e2e-ui suites.\n *\n * The Verdaccio UI behaves differently across branches — search result\n * payload shape differs between the `/-/v1/search` and\n * `/-/verdaccio/data/search/*` endpoints, the language picker was\n * added in a specific minor, etc. Rather than forking the suite per\n * branch, consumers can disable individual tests via\n * `createRegistryConfig({ features: { … } })`.\n *\n * Most flags default to `true` (test runs). Compatibility probes for\n * unreleased server behavior may default to `false`; enable them in\n * consumers that run against a Verdaccio build with the feature.\n * A disabled flag converts the test into a Mocha `it.skip` call — the\n * suite still reports the test but marks it as pending.\n */\nexport interface Features {\n search: {\n /**\n * Whether to run the \"results dropdown renders a matching\n * package\" test. Disable on builds where the search result shape\n * or the Autocomplete rendering differs from the default assumption.\n */\n resultsDropdown: boolean;\n /**\n * Whether to run the \"clicking a result navigates to detail\" test.\n * Depends on the Autocomplete `onSelectItem` → router wiring.\n */\n resultClickNavigation: boolean;\n };\n home: {\n /**\n * Whether to run the `with a published package` sub-block inside\n * `homeTests` (publishes a throwaway package and asserts it\n * renders in the package list).\n */\n publishedPackageRendering: boolean;\n };\n settings: {\n /**\n * Whether to run the \"change language via card click\" test.\n * Depends on the `LanguageSwitch` component layout and its\n * translation sentinels (\"Translations\", \"German\"). Skip on\n * builds that lag behind the upstream translation file.\n */\n languageSwitcher: boolean;\n };\n signin: {\n /**\n * Whether to run the three validation tests (disabled submit\n * button, invalid credentials error banner). Depends on the\n * current yup schema and the hard-coded \"Invalid username or\n * password\" error string.\n */\n validationTests: boolean;\n };\n layout: {\n /**\n * Whether to run the dark/light theme switch test. Depends on\n * `web.showThemeSwitch` (defaults to true in ui-theme) and the\n * `header--button--light` / `header--button--dark` testids.\n */\n themeSwitch: boolean;\n };\n publish: {\n /**\n * Whether to run the \"tarball download button fires a GET\" test.\n * Depends on `web.showDownloadTarball` (defaults to true) and\n * the published package manifest having a valid `dist.tarball`.\n */\n downloadTarball: boolean;\n /**\n * Whether to run the private package tarball download tests from\n * the Web UI package list and sidebar.\n *\n * Enable only on Verdaccio builds that accept Web UI bearer tokens\n * on protected npm package/tarball routes. Verdaccio 6.x currently\n * returns HTTP 401 for this flow; the regression is tracked in\n * verdaccio/verdaccio#5765.\n */\n privateDownloadTarball: boolean;\n /**\n * Whether to run the \"raw viewer dialog opens + closes\" test.\n * Depends on `web.showRaw` (defaults to true).\n */\n rawViewer: boolean;\n };\n changePassword: {\n /**\n * Whether to run the happy-path test (submit valid change,\n * expect navigation to the success page, then restore the\n * original password in `after()`).\n *\n * The suite targets /-/web/change-password, which renders only\n * when the server is configured with `flags.changePassword: true`.\n * Disable on registries that do not enable the flag.\n *\n * Also disable on **published verdaccio 6.x** (all lines through\n * 6.5.0): the reset_password handler in\n * `verdaccio/build/api/web/api/user.js` ships with an inverted\n * conditional — `validatePassword(...) === false` gates the\n * `auth.changePassword(...)` call, so a *valid* new password\n * always returns HTTP 400 (`PASSWORD_VALIDATION`). The bug is\n * fixed on the development branch but has not been released\n * in any 6.x tag, so the happy path cannot succeed against an\n * `npm install verdaccio@6` runtime.\n */\n happyPath: boolean;\n /**\n * Whether to run the client-side validation tests (submit button\n * stays disabled while fields are empty / mismatched confirm).\n * Depends on the yup `changePasswordSchema`.\n */\n validation: boolean;\n /**\n * Whether to run the \"wrong old password shows error banner\" test.\n * Depends on the server rejecting the call and the onSubmit catch\n * block surfacing `\"Failed to change password\"` via\n * `LoginDialogFormError`.\n */\n wrongOldPassword: boolean;\n };\n}\n\n/** Defaults: all flags on. */\nexport const DEFAULT_FEATURES: Features = {\n search: {\n resultsDropdown: true,\n resultClickNavigation: true,\n },\n home: {\n publishedPackageRendering: true,\n },\n settings: {\n languageSwitcher: true,\n },\n signin: {\n validationTests: true,\n },\n layout: {\n themeSwitch: true,\n },\n publish: {\n downloadTarball: true,\n privateDownloadTarball: false,\n rawViewer: true,\n },\n changePassword: {\n happyPath: true,\n validation: true,\n wrongOldPassword: true,\n },\n};\n\n/**\n * Merge user overrides into the default feature flags. Per-section,\n * one level deep — matching the style of `mergeTestIds`.\n */\nexport function mergeFeatures(defaults: Features, overrides?: DeepPartial<Features>): Features {\n if (!overrides) return defaults;\n return {\n search: { ...defaults.search, ...overrides.search },\n home: { ...defaults.home, ...overrides.home },\n settings: { ...defaults.settings, ...overrides.settings },\n signin: { ...defaults.signin, ...overrides.signin },\n layout: { ...defaults.layout, ...overrides.layout },\n publish: { ...defaults.publish, ...overrides.publish },\n changePassword: { ...defaults.changePassword, ...overrides.changePassword },\n };\n}\n\n/**\n * Helper that returns either `it` or `it.skip` depending on an\n * enabled flag. Usage:\n *\n * maybeIt(features.search.resultsDropdown)('…', () => { … });\n */\nexport function maybeIt(enabled: boolean): Mocha.TestFunction | Mocha.PendingTestFunction {\n return enabled ? it : it.skip;\n}\n"],"mappings":";;AAgIA,IAAa,mBAA6B;CACxC,QAAQ;EACN,iBAAiB;EACjB,uBAAuB;CACzB;CACA,MAAM,EACJ,2BAA2B,KAC7B;CACA,UAAU,EACR,kBAAkB,KACpB;CACA,QAAQ,EACN,iBAAiB,KACnB;CACA,QAAQ,EACN,aAAa,KACf;CACA,SAAS;EACP,iBAAiB;EACjB,wBAAwB;EACxB,WAAW;CACb;CACA,gBAAgB;EACd,WAAW;EACX,YAAY;EACZ,kBAAkB;CACpB;AACF;;;;;AAMA,SAAgB,cAAc,UAAoB,WAA6C;CAC7F,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO;EACL,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,MAAM;GAAE,GAAG,SAAS;GAAM,GAAG,UAAU;EAAK;EAC5C,UAAU;GAAE,GAAG,SAAS;GAAU,GAAG,UAAU;EAAS;EACxD,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,UAAU;EAAQ;EACrD,gBAAgB;GAAE,GAAG,SAAS;GAAgB,GAAG,UAAU;EAAe;CAC5E;AACF;;;;;;;AAQA,SAAgB,QAAQ,SAAkE;CACxF,OAAO,UAAU,KAAK,GAAG;AAC3B"}
1
+ {"version":3,"file":"features.cjs","names":[],"sources":["../../src/features.ts"],"sourcesContent":["import type { DeepPartial } from './testIds';\n\n/**\n * Per-test feature flags for enabling/disabling individual test cases\n * in the e2e-ui suites.\n *\n * The Verdaccio UI behaves differently across branches — search result\n * payload shape differs between the `/-/v1/search` and\n * `/-/verdaccio/data/search/*` endpoints, the language picker was\n * added in a specific minor, etc. Rather than forking the suite per\n * branch, consumers can disable individual tests via\n * `createRegistryConfig({ features: { … } })`.\n *\n * Most flags default to `true` (test runs). Compatibility probes for\n * unreleased server behavior may default to `false`; enable them in\n * consumers that run against a Verdaccio build with the feature.\n * A disabled flag converts the test into a Mocha `it.skip` call — the\n * suite still reports the test but marks it as pending.\n */\nexport interface Features {\n search: {\n /**\n * Whether to run the \"results dropdown renders a matching\n * package\" test. Disable on builds where the search result shape\n * or the Autocomplete rendering differs from the default assumption.\n */\n resultsDropdown: boolean;\n /**\n * Whether to run the \"clicking a result navigates to detail\" test.\n * Depends on the Autocomplete `onSelectItem` → router wiring.\n */\n resultClickNavigation: boolean;\n };\n home: {\n /**\n * Whether to run the `with a published package` sub-block inside\n * `homeTests` (publishes a throwaway package and asserts it\n * renders in the package list).\n */\n publishedPackageRendering: boolean;\n };\n settings: {\n /**\n * Whether to run the \"change language via card click\" test.\n * Depends on the `LanguageSwitch` component layout and its\n * translation sentinels (\"Translations\", \"German\"). Skip on\n * builds that lag behind the upstream translation file.\n */\n languageSwitcher: boolean;\n };\n signin: {\n /**\n * Whether to run the three validation tests (disabled submit\n * button, invalid credentials error banner). Depends on the\n * current yup schema and the hard-coded \"Invalid username or\n * password\" error string.\n */\n validationTests: boolean;\n };\n layout: {\n /**\n * Whether to run the dark/light theme switch test. Depends on\n * `web.showThemeSwitch` (defaults to true in ui-theme) and the\n * `header--button--light` / `header--button--dark` testids.\n */\n themeSwitch: boolean;\n };\n publish: {\n /**\n * Whether to run the \"tarball download button fires a GET\" test.\n * Depends on `web.showDownloadTarball` (defaults to true) and\n * the published package manifest having a valid `dist.tarball`.\n */\n downloadTarball: boolean;\n /**\n * Whether to run the private package tarball download tests from\n * the Web UI package list and sidebar.\n *\n * Enable only on Verdaccio builds that accept Web UI bearer tokens\n * on protected npm package/tarball routes. Verdaccio 6.x currently\n * returns HTTP 401 for this flow; the regression is tracked in\n * verdaccio/verdaccio#5765.\n */\n privateDownloadTarball: boolean;\n /**\n * Whether to run the \"raw viewer dialog opens + closes\" test.\n * Depends on `web.showRaw` (defaults to true).\n */\n rawViewer: boolean;\n };\n detail: {\n /**\n * Whether to run the \"visiting /v/<version> pins that version\" test\n * (asserts the sidebar request carries `?v=<version>`).\n */\n versionPinned: boolean;\n /**\n * Whether to run the wire-format test for scoped packages: the UI\n * must request `/-/verdaccio/data/sidebar/@scope/name` with the `@`\n * and `/` literal (npm registry convention), never `%40`-encoded.\n * Pins the regression caught by these suites in\n * verdaccio/verdaccio#6210.\n */\n scopedWireFormat: boolean;\n /**\n * Whether to run the \"nonexistent version renders Not Found\" test.\n * Requires the server to answer 404 for unknown `?v=` values\n * (verdaccio/verdaccio#6210); released lines silently fall back to\n * `latest`, so this defaults to `false` as a probe.\n */\n versionNotFound: boolean;\n };\n failureModes: {\n /**\n * Whether to run the \"backend 5xx on the package list must not\n * render the empty-registry onboarding\" test. Holds on every line\n * (a 5xx carries an HTTP code the UI always treated as an error).\n */\n homeServerError: boolean;\n /**\n * Whether to run the network-failure variant (request never\n * reaches the server). Released UIs treat a network failure as an\n * empty registry and show the onboarding card\n * (verdaccio/verdaccio#6210 fixes it), so this defaults to `false`\n * as a probe.\n */\n homeNetworkError: boolean;\n /**\n * Whether to run the \"5xx on the detail page renders an error\n * state instead of a blank page\" test. Requires the generic error\n * page from verdaccio/verdaccio#6210; released UIs render blank,\n * so this defaults to `false` as a probe.\n */\n detailErrorState: boolean;\n };\n i18n: {\n /**\n * Whether to scan the home and detail pages for raw i18n keys\n * (visible text like `sidebar.detail.version`). Holds on every\n * released line — these pages ship fully translated.\n */\n noRawKeysCorePages: boolean;\n /**\n * Whether to scan the security pages (login dialog, /-/web/login,\n * add-user, change-password) for raw i18n keys. The whole\n * `security.*` namespace is missing from every published ui-theme\n * bundle (fixed in verdaccio/verdaccio#6210), so this defaults to\n * `false` as a probe.\n */\n noRawKeysSecurityPages: boolean;\n };\n signup: {\n /**\n * Whether to run the create-user happy path (fill the form on\n * /-/web/add-user, submit, land on the success page) and assert\n * the wire contract: `PUT /-/verdaccio/sec/signup` with a 36-char\n * `sessionId`. Requires the server flag `createUser: true` AND the\n * fixed signup form from verdaccio/verdaccio#6210 — every\n * published ui-theme posts to a URL that only serves the SPA, so\n * this defaults to `false` as a probe.\n */\n happyPath: boolean;\n /**\n * Whether to run the client-side validation tests on the add-user\n * form (invalid email shows a message, url-unsafe username blocks\n * submit). Same requirements as `happyPath`.\n */\n validation: boolean;\n };\n changePassword: {\n /**\n * Whether to run the happy-path test (submit valid change,\n * expect navigation to the success page, then restore the\n * original password in `after()`).\n *\n * The suite targets /-/web/change-password, which renders only\n * when the server is configured with `flags.changePassword: true`.\n * Disable on registries that do not enable the flag.\n *\n * Also disable on **published verdaccio 6.x** (all lines through\n * 6.5.0): the reset_password handler in\n * `verdaccio/build/api/web/api/user.js` ships with an inverted\n * conditional — `validatePassword(...) === false` gates the\n * `auth.changePassword(...)` call, so a *valid* new password\n * always returns HTTP 400 (`PASSWORD_VALIDATION`). The bug is\n * fixed on the development branch but has not been released\n * in any 6.x tag, so the happy path cannot succeed against an\n * `npm install verdaccio@6` runtime.\n */\n happyPath: boolean;\n /**\n * Whether to run the client-side validation tests (submit button\n * stays disabled while fields are empty / mismatched confirm).\n * Depends on the yup `changePasswordSchema`.\n */\n validation: boolean;\n /**\n * Whether to run the \"wrong old password shows error banner\" test.\n * Depends on the server rejecting the call and the onSubmit catch\n * block surfacing `\"Failed to change password\"` via\n * `LoginDialogFormError`.\n */\n wrongOldPassword: boolean;\n };\n}\n\n/** Defaults: all flags on. */\nexport const DEFAULT_FEATURES: Features = {\n search: {\n resultsDropdown: true,\n resultClickNavigation: true,\n },\n home: {\n publishedPackageRendering: true,\n },\n settings: {\n languageSwitcher: true,\n },\n signin: {\n validationTests: true,\n },\n layout: {\n themeSwitch: true,\n },\n publish: {\n downloadTarball: true,\n privateDownloadTarball: false,\n rawViewer: true,\n },\n detail: {\n versionPinned: true,\n scopedWireFormat: true,\n versionNotFound: false,\n },\n failureModes: {\n homeServerError: true,\n homeNetworkError: false,\n detailErrorState: false,\n },\n i18n: {\n noRawKeysCorePages: true,\n noRawKeysSecurityPages: false,\n },\n signup: {\n happyPath: false,\n validation: false,\n },\n changePassword: {\n happyPath: true,\n validation: true,\n wrongOldPassword: true,\n },\n};\n\n/**\n * Merge user overrides into the default feature flags. Per-section,\n * one level deep — matching the style of `mergeTestIds`.\n */\nexport function mergeFeatures(defaults: Features, overrides?: DeepPartial<Features>): Features {\n if (!overrides) return defaults;\n return {\n search: { ...defaults.search, ...overrides.search },\n home: { ...defaults.home, ...overrides.home },\n settings: { ...defaults.settings, ...overrides.settings },\n signin: { ...defaults.signin, ...overrides.signin },\n layout: { ...defaults.layout, ...overrides.layout },\n publish: { ...defaults.publish, ...overrides.publish },\n detail: { ...defaults.detail, ...overrides.detail },\n failureModes: { ...defaults.failureModes, ...overrides.failureModes },\n i18n: { ...defaults.i18n, ...overrides.i18n },\n signup: { ...defaults.signup, ...overrides.signup },\n changePassword: { ...defaults.changePassword, ...overrides.changePassword },\n };\n}\n\n/**\n * Helper that returns either `it` or `it.skip` depending on an\n * enabled flag. Usage:\n *\n * maybeIt(features.search.resultsDropdown)('…', () => { … });\n */\nexport function maybeIt(enabled: boolean): Mocha.TestFunction | Mocha.PendingTestFunction {\n return enabled ? it : it.skip;\n}\n"],"mappings":";;AA+MA,IAAa,mBAA6B;CACxC,QAAQ;EACN,iBAAiB;EACjB,uBAAuB;CACzB;CACA,MAAM,EACJ,2BAA2B,KAC7B;CACA,UAAU,EACR,kBAAkB,KACpB;CACA,QAAQ,EACN,iBAAiB,KACnB;CACA,QAAQ,EACN,aAAa,KACf;CACA,SAAS;EACP,iBAAiB;EACjB,wBAAwB;EACxB,WAAW;CACb;CACA,QAAQ;EACN,eAAe;EACf,kBAAkB;EAClB,iBAAiB;CACnB;CACA,cAAc;EACZ,iBAAiB;EACjB,kBAAkB;EAClB,kBAAkB;CACpB;CACA,MAAM;EACJ,oBAAoB;EACpB,wBAAwB;CAC1B;CACA,QAAQ;EACN,WAAW;EACX,YAAY;CACd;CACA,gBAAgB;EACd,WAAW;EACX,YAAY;EACZ,kBAAkB;CACpB;AACF;;;;;AAMA,SAAgB,cAAc,UAAoB,WAA6C;CAC7F,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO;EACL,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,MAAM;GAAE,GAAG,SAAS;GAAM,GAAG,UAAU;EAAK;EAC5C,UAAU;GAAE,GAAG,SAAS;GAAU,GAAG,UAAU;EAAS;EACxD,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,UAAU;EAAQ;EACrD,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,cAAc;GAAE,GAAG,SAAS;GAAc,GAAG,UAAU;EAAa;EACpE,MAAM;GAAE,GAAG,SAAS;GAAM,GAAG,UAAU;EAAK;EAC5C,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,gBAAgB;GAAE,GAAG,SAAS;GAAgB,GAAG,UAAU;EAAe;CAC5E;AACF;;;;;;;AAQA,SAAgB,QAAQ,SAAkE;CACxF,OAAO,UAAU,KAAK,GAAG;AAC3B"}
@@ -9,6 +9,11 @@ const require_search = require("./tests/search.cjs");
9
9
  const require_settings = require("./tests/settings.cjs");
10
10
  const require_layout = require("./tests/layout.cjs");
11
11
  const require_change_password = require("./tests/change-password.cjs");
12
+ const require_detail = require("./tests/detail.cjs");
13
+ const require_failure_modes = require("./tests/failure-modes.cjs");
14
+ const require_i18n_keys = require("./tests/i18n-keys.cjs");
15
+ const require_signup = require("./tests/signup.cjs");
16
+ require("./tests/index.cjs");
12
17
  let fs = require("fs");
13
18
  //#region src/index.ts
14
19
  /**
@@ -161,6 +166,10 @@ function registerAllTests(config) {
161
166
  require_search.searchTests(config);
162
167
  require_settings.settingsTests(config);
163
168
  require_publish$1.publishTests(config);
169
+ require_detail.detailTests(config);
170
+ require_failure_modes.failureModeTests(config);
171
+ require_i18n_keys.i18nKeyTests(config);
172
+ require_signup.signupTests(config);
164
173
  require_change_password.changePasswordTests(config);
165
174
  }
166
175
  //#endregion
@@ -170,7 +179,10 @@ exports.DEFAULT_TEST_IDS = require_testIds.DEFAULT_TEST_IDS;
170
179
  exports.changePasswordTests = require_change_password.changePasswordTests;
171
180
  exports.cleanupPublished = require_publish.cleanupPublished;
172
181
  exports.createRegistryConfig = createRegistryConfig;
182
+ exports.detailTests = require_detail.detailTests;
183
+ exports.failureModeTests = require_failure_modes.failureModeTests;
173
184
  exports.homeTests = require_home.homeTests;
185
+ exports.i18nKeyTests = require_i18n_keys.i18nKeyTests;
174
186
  exports.layoutTests = require_layout.layoutTests;
175
187
  exports.maybeIt = require_features.maybeIt;
176
188
  exports.publishPackage = require_publish.publishPackage;
@@ -180,6 +192,7 @@ exports.searchTests = require_search.searchTests;
180
192
  exports.settingsTests = require_settings.settingsTests;
181
193
  exports.setupVerdaccioTasks = setupVerdaccioTasks;
182
194
  exports.signinTests = require_signin.signinTests;
195
+ exports.signupTests = require_signup.signupTests;
183
196
  exports.unpublishPackage = require_publish.unpublishPackage;
184
197
 
185
198
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { appendFileSync } from 'fs';\n\nimport { DEFAULT_FEATURES, mergeFeatures } from './features';\nimport {\n PublishPackageResult,\n PublishPackageTaskInput,\n UnpublishPackageInput,\n UnpublishPackageResult,\n cleanupPublished,\n publishPackage,\n unpublishPackage,\n} from './tasks';\nimport { DEFAULT_SELECTORS, DEFAULT_TEST_IDS, mergeSelectors, mergeTestIds } from './testIds';\n// Re-export for convenience\nimport {\n changePasswordTests,\n homeTests,\n layoutTests,\n publishTests,\n searchTests,\n settingsTests,\n signinTests,\n} from './tests';\nimport { RegistryConfig, RegistryTaskResult, VerdaccioUiOptions } from './types';\n\nexport type { RegistryConfig, RegistryTaskResult, VerdaccioUiOptions } from './types';\nexport type {\n PublishPackageInput,\n PublishPackageTaskInput,\n PublishPackageResult,\n UnpublishPackageInput,\n UnpublishPackageResult,\n} from './tasks';\nexport type { DeepPartial, Selectors, TestIds } from './testIds';\nexport type { Features } from './features';\nexport { DEFAULT_SELECTORS, DEFAULT_TEST_IDS } from './testIds';\nexport { DEFAULT_FEATURES, maybeIt } from './features';\nexport { publishPackage, cleanupPublished, unpublishPackage } from './tasks';\nexport {\n homeTests,\n signinTests,\n publishTests,\n searchTests,\n settingsTests,\n layoutTests,\n changePasswordTests,\n} from './tests';\n\n/**\n * Build a full RegistryConfig from user-provided options with defaults.\n *\n * `testIds` and `selectors` are deep-merged per section with the\n * defaults in `./testIds`. Consumers targeting a non-default Verdaccio\n * build can override just the fields that drifted:\n *\n * createRegistryConfig({\n * registryUrl: 'http://localhost:4873',\n * testIds: {\n * header: { settingsTooltip: 'my-new-settings-btn' },\n * },\n * });\n */\nexport function createRegistryConfig(options: VerdaccioUiOptions): RegistryConfig {\n const url = new URL(options.registryUrl);\n return {\n registryUrl: options.registryUrl,\n port: options.port ?? (parseInt(url.port, 10) || 4873),\n credentials: options.credentials ?? { user: 'test', password: 'test' },\n title: options.title ?? 'Verdaccio',\n testIds: mergeTestIds(DEFAULT_TEST_IDS, options.testIds),\n selectors: mergeSelectors(DEFAULT_SELECTORS, options.selectors),\n features: mergeFeatures(DEFAULT_FEATURES, options.features),\n };\n}\n\n/**\n * Register Verdaccio Cypress tasks in setupNodeEvents.\n *\n * Usage in cypress.config.ts:\n * import { setupVerdaccioTasks } from '@verdaccio/e2e-ui';\n *\n * export default defineConfig({\n * e2e: {\n * setupNodeEvents(on) {\n * setupVerdaccioTasks(on, { registryUrl: 'http://localhost:4873' });\n * },\n * },\n * });\n */\nexport function setupVerdaccioTasks(on: Cypress.PluginEvents, options: VerdaccioUiOptions): void {\n const config = createRegistryConfig(options);\n\n // GitHub Actions Step Summary\n on('after:run', (results) => {\n const summaryFile = process.env.GITHUB_STEP_SUMMARY;\n if (!summaryFile || !results || !('runs' in results)) return;\n\n const cypressResults = results as CypressCommandLine.CypressRunResult;\n const lines: string[] = [];\n\n lines.push('## UI E2E Test Results\\n');\n lines.push('| Spec | Tests | Passing | Failing | Pending | Duration |');\n lines.push('|------|-------|---------|---------|---------|----------|');\n\n for (const run of cypressResults.runs) {\n const specName = run.spec.relative || run.spec.name;\n const stats = run.stats;\n const icon = stats.failures > 0 ? '❌' : '✅';\n const dur = stats.duration ? `${(stats.duration / 1000).toFixed(1)}s` : '-';\n lines.push(\n `| ${icon} ${specName} | ${stats.tests} | ${stats.passes} | ${stats.failures} | ${stats.pending} | ${dur} |`\n );\n }\n\n lines.push('');\n const totals = cypressResults.totalTests ?? 0;\n const passed = cypressResults.totalPassed ?? 0;\n const failed = cypressResults.totalFailed ?? 0;\n const pending = cypressResults.totalPending ?? 0;\n const emoji = failed > 0 ? '❌' : '✅';\n lines.push(\n `${emoji} **${passed} passed**, **${failed} failed**, **${pending} pending** (${totals} total)\\n`\n );\n\n appendFileSync(summaryFile, lines.join('\\n'));\n });\n\n on('task', {\n registry(): RegistryTaskResult {\n return {\n registryUrl: config.registryUrl,\n port: config.port,\n };\n },\n /**\n * Publish a throwaway package to the registry.\n *\n * Usage from a Cypress spec:\n *\n * cy.task('publishPackage', { pkgName: '@verdaccio/pkg-scoped' });\n *\n * Any field not provided falls back to the values configured here\n * (registryUrl, credentials). The task returns a PublishPackageResult.\n */\n async publishPackage(input: PublishPackageTaskInput): Promise<PublishPackageResult> {\n return publishPackage({\n registryUrl: input.registryUrl ?? config.registryUrl,\n credentials: input.credentials ?? config.credentials,\n pkgName: input.pkgName,\n version: input.version,\n dependencies: input.dependencies,\n devDependencies: input.devDependencies,\n unique: input.unique,\n });\n },\n /**\n * Remove a temp project folder returned by a previous publishPackage\n * call. Returns null (Cypress tasks must return something serializable).\n *\n * cy.task('cleanupPublished', result.tempFolder);\n */\n async cleanupPublished(tempFolder: string): Promise<null> {\n await cleanupPublished(tempFolder);\n return null;\n },\n /**\n * Unpublish a package from the registry so the next test starts\n * from a clean slate. Accepts either a package name or a full input\n * object (to reuse a prior tempFolder's token).\n *\n * cy.task('unpublishPackage', '@verdaccio/pkg-scoped');\n * cy.task('unpublishPackage', { pkgName, tempFolder });\n */\n async unpublishPackage(\n input:\n | string\n | (Omit<UnpublishPackageInput, 'registryUrl'> & {\n registryUrl?: string;\n })\n ): Promise<UnpublishPackageResult> {\n const normalized =\n typeof input === 'string'\n ? { pkgName: input, registryUrl: config.registryUrl }\n : { ...input, registryUrl: input.registryUrl ?? config.registryUrl };\n return unpublishPackage(normalized);\n },\n });\n}\n\n/**\n * Register all Verdaccio UI tests.\n *\n * Usage in a spec file:\n * import { registerAllTests, createRegistryConfig } from '@verdaccio/e2e-ui';\n *\n * const config = createRegistryConfig({ registryUrl: 'http://localhost:4873' });\n * registerAllTests(config);\n *\n * Or pick individual suites:\n * import { homeTests, signinTests } from '@verdaccio/e2e-ui';\n *\n * const config = createRegistryConfig({ registryUrl: 'http://localhost:4873' });\n * homeTests(config);\n * signinTests(config);\n */\nexport function registerAllTests(config: RegistryConfig): void {\n homeTests(config);\n signinTests(config);\n layoutTests(config);\n searchTests(config);\n settingsTests(config);\n publishTests(config);\n changePasswordTests(config);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,SAAgB,qBAAqB,SAA6C;CAChF,MAAM,MAAM,IAAI,IAAI,QAAQ,WAAW;CACvC,OAAO;EACL,aAAa,QAAQ;EACrB,MAAM,QAAQ,SAAS,SAAS,IAAI,MAAM,EAAE,KAAK;EACjD,aAAa,QAAQ,eAAe;GAAE,MAAM;GAAQ,UAAU;EAAO;EACrE,OAAO,QAAQ,SAAS;EACxB,SAAS,gBAAA,aAAa,gBAAA,kBAAkB,QAAQ,OAAO;EACvD,WAAW,gBAAA,eAAe,gBAAA,mBAAmB,QAAQ,SAAS;EAC9D,UAAU,iBAAA,cAAc,iBAAA,kBAAkB,QAAQ,QAAQ;CAC5D;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,oBAAoB,IAA0B,SAAmC;CAC/F,MAAM,SAAS,qBAAqB,OAAO;CAG3C,GAAG,cAAc,YAAY;EAC3B,MAAM,cAAc,QAAQ,IAAI;EAChC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,UAAU,UAAU;EAEtD,MAAM,iBAAiB;EACvB,MAAM,QAAkB,CAAC;EAEzB,MAAM,KAAK,0BAA0B;EACrC,MAAM,KAAK,2DAA2D;EACtE,MAAM,KAAK,2DAA2D;EAEtE,KAAK,MAAM,OAAO,eAAe,MAAM;GACrC,MAAM,WAAW,IAAI,KAAK,YAAY,IAAI,KAAK;GAC/C,MAAM,QAAQ,IAAI;GAClB,MAAM,OAAO,MAAM,WAAW,IAAI,MAAM;GACxC,MAAM,MAAM,MAAM,WAAW,IAAI,MAAM,WAAW,IAAA,CAAM,QAAQ,CAAC,EAAE,KAAK;GACxE,MAAM,KACJ,KAAK,KAAK,GAAG,SAAS,KAAK,MAAM,MAAM,KAAK,MAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,QAAQ,KAAK,IAAI,GAC3G;EACF;EAEA,MAAM,KAAK,EAAE;EACb,MAAM,SAAS,eAAe,cAAc;EAC5C,MAAM,SAAS,eAAe,eAAe;EAC7C,MAAM,SAAS,eAAe,eAAe;EAC7C,MAAM,UAAU,eAAe,gBAAgB;EAC/C,MAAM,QAAQ,SAAS,IAAI,MAAM;EACjC,MAAM,KACJ,GAAG,MAAM,KAAK,OAAO,eAAe,OAAO,eAAe,QAAQ,cAAc,OAAO,UACzF;EAEA,CAAA,GAAA,GAAA,eAAA,CAAe,aAAa,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;CAED,GAAG,QAAQ;EACT,WAA+B;GAC7B,OAAO;IACL,aAAa,OAAO;IACpB,MAAM,OAAO;GACf;EACF;;;;;;;;;;;EAWA,MAAM,eAAe,OAA+D;GAClF,OAAO,gBAAA,eAAe;IACpB,aAAa,MAAM,eAAe,OAAO;IACzC,aAAa,MAAM,eAAe,OAAO;IACzC,SAAS,MAAM;IACf,SAAS,MAAM;IACf,cAAc,MAAM;IACpB,iBAAiB,MAAM;IACvB,QAAQ,MAAM;GAChB,CAAC;EACH;;;;;;;EAOA,MAAM,iBAAiB,YAAmC;GACxD,MAAM,gBAAA,iBAAiB,UAAU;GACjC,OAAO;EACT;;;;;;;;;EASA,MAAM,iBACJ,OAKiC;GACjC,MAAM,aACJ,OAAO,UAAU,WACb;IAAE,SAAS;IAAO,aAAa,OAAO;GAAY,IAClD;IAAE,GAAG;IAAO,aAAa,MAAM,eAAe,OAAO;GAAY;GACvE,OAAO,gBAAA,iBAAiB,UAAU;EACpC;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;AAkBA,SAAgB,iBAAiB,QAA8B;CAC7D,aAAA,UAAU,MAAM;CAChB,eAAA,YAAY,MAAM;CAClB,eAAA,YAAY,MAAM;CAClB,eAAA,YAAY,MAAM;CAClB,iBAAA,cAAc,MAAM;CACpB,kBAAA,aAAa,MAAM;CACnB,wBAAA,oBAAoB,MAAM;AAC5B"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { appendFileSync } from 'fs';\n\nimport { DEFAULT_FEATURES, mergeFeatures } from './features';\nimport {\n PublishPackageResult,\n PublishPackageTaskInput,\n UnpublishPackageInput,\n UnpublishPackageResult,\n cleanupPublished,\n publishPackage,\n unpublishPackage,\n} from './tasks';\nimport { DEFAULT_SELECTORS, DEFAULT_TEST_IDS, mergeSelectors, mergeTestIds } from './testIds';\n// Re-export for convenience\nimport {\n changePasswordTests,\n detailTests,\n failureModeTests,\n homeTests,\n i18nKeyTests,\n layoutTests,\n publishTests,\n searchTests,\n settingsTests,\n signinTests,\n signupTests,\n} from './tests';\nimport { RegistryConfig, RegistryTaskResult, VerdaccioUiOptions } from './types';\n\nexport type { RegistryConfig, RegistryTaskResult, VerdaccioUiOptions } from './types';\nexport type {\n PublishPackageInput,\n PublishPackageTaskInput,\n PublishPackageResult,\n UnpublishPackageInput,\n UnpublishPackageResult,\n} from './tasks';\nexport type { DeepPartial, Selectors, TestIds } from './testIds';\nexport type { Features } from './features';\nexport { DEFAULT_SELECTORS, DEFAULT_TEST_IDS } from './testIds';\nexport { DEFAULT_FEATURES, maybeIt } from './features';\nexport { publishPackage, cleanupPublished, unpublishPackage } from './tasks';\nexport {\n homeTests,\n signinTests,\n publishTests,\n searchTests,\n settingsTests,\n layoutTests,\n changePasswordTests,\n detailTests,\n failureModeTests,\n i18nKeyTests,\n signupTests,\n} from './tests';\n\n/**\n * Build a full RegistryConfig from user-provided options with defaults.\n *\n * `testIds` and `selectors` are deep-merged per section with the\n * defaults in `./testIds`. Consumers targeting a non-default Verdaccio\n * build can override just the fields that drifted:\n *\n * createRegistryConfig({\n * registryUrl: 'http://localhost:4873',\n * testIds: {\n * header: { settingsTooltip: 'my-new-settings-btn' },\n * },\n * });\n */\nexport function createRegistryConfig(options: VerdaccioUiOptions): RegistryConfig {\n const url = new URL(options.registryUrl);\n return {\n registryUrl: options.registryUrl,\n port: options.port ?? (parseInt(url.port, 10) || 4873),\n credentials: options.credentials ?? { user: 'test', password: 'test' },\n title: options.title ?? 'Verdaccio',\n testIds: mergeTestIds(DEFAULT_TEST_IDS, options.testIds),\n selectors: mergeSelectors(DEFAULT_SELECTORS, options.selectors),\n features: mergeFeatures(DEFAULT_FEATURES, options.features),\n };\n}\n\n/**\n * Register Verdaccio Cypress tasks in setupNodeEvents.\n *\n * Usage in cypress.config.ts:\n * import { setupVerdaccioTasks } from '@verdaccio/e2e-ui';\n *\n * export default defineConfig({\n * e2e: {\n * setupNodeEvents(on) {\n * setupVerdaccioTasks(on, { registryUrl: 'http://localhost:4873' });\n * },\n * },\n * });\n */\nexport function setupVerdaccioTasks(on: Cypress.PluginEvents, options: VerdaccioUiOptions): void {\n const config = createRegistryConfig(options);\n\n // GitHub Actions Step Summary\n on('after:run', (results) => {\n const summaryFile = process.env.GITHUB_STEP_SUMMARY;\n if (!summaryFile || !results || !('runs' in results)) return;\n\n const cypressResults = results as CypressCommandLine.CypressRunResult;\n const lines: string[] = [];\n\n lines.push('## UI E2E Test Results\\n');\n lines.push('| Spec | Tests | Passing | Failing | Pending | Duration |');\n lines.push('|------|-------|---------|---------|---------|----------|');\n\n for (const run of cypressResults.runs) {\n const specName = run.spec.relative || run.spec.name;\n const stats = run.stats;\n const icon = stats.failures > 0 ? '❌' : '✅';\n const dur = stats.duration ? `${(stats.duration / 1000).toFixed(1)}s` : '-';\n lines.push(\n `| ${icon} ${specName} | ${stats.tests} | ${stats.passes} | ${stats.failures} | ${stats.pending} | ${dur} |`\n );\n }\n\n lines.push('');\n const totals = cypressResults.totalTests ?? 0;\n const passed = cypressResults.totalPassed ?? 0;\n const failed = cypressResults.totalFailed ?? 0;\n const pending = cypressResults.totalPending ?? 0;\n const emoji = failed > 0 ? '❌' : '✅';\n lines.push(\n `${emoji} **${passed} passed**, **${failed} failed**, **${pending} pending** (${totals} total)\\n`\n );\n\n appendFileSync(summaryFile, lines.join('\\n'));\n });\n\n on('task', {\n registry(): RegistryTaskResult {\n return {\n registryUrl: config.registryUrl,\n port: config.port,\n };\n },\n /**\n * Publish a throwaway package to the registry.\n *\n * Usage from a Cypress spec:\n *\n * cy.task('publishPackage', { pkgName: '@verdaccio/pkg-scoped' });\n *\n * Any field not provided falls back to the values configured here\n * (registryUrl, credentials). The task returns a PublishPackageResult.\n */\n async publishPackage(input: PublishPackageTaskInput): Promise<PublishPackageResult> {\n return publishPackage({\n registryUrl: input.registryUrl ?? config.registryUrl,\n credentials: input.credentials ?? config.credentials,\n pkgName: input.pkgName,\n version: input.version,\n dependencies: input.dependencies,\n devDependencies: input.devDependencies,\n unique: input.unique,\n });\n },\n /**\n * Remove a temp project folder returned by a previous publishPackage\n * call. Returns null (Cypress tasks must return something serializable).\n *\n * cy.task('cleanupPublished', result.tempFolder);\n */\n async cleanupPublished(tempFolder: string): Promise<null> {\n await cleanupPublished(tempFolder);\n return null;\n },\n /**\n * Unpublish a package from the registry so the next test starts\n * from a clean slate. Accepts either a package name or a full input\n * object (to reuse a prior tempFolder's token).\n *\n * cy.task('unpublishPackage', '@verdaccio/pkg-scoped');\n * cy.task('unpublishPackage', { pkgName, tempFolder });\n */\n async unpublishPackage(\n input:\n | string\n | (Omit<UnpublishPackageInput, 'registryUrl'> & {\n registryUrl?: string;\n })\n ): Promise<UnpublishPackageResult> {\n const normalized =\n typeof input === 'string'\n ? { pkgName: input, registryUrl: config.registryUrl }\n : { ...input, registryUrl: input.registryUrl ?? config.registryUrl };\n return unpublishPackage(normalized);\n },\n });\n}\n\n/**\n * Register all Verdaccio UI tests.\n *\n * Usage in a spec file:\n * import { registerAllTests, createRegistryConfig } from '@verdaccio/e2e-ui';\n *\n * const config = createRegistryConfig({ registryUrl: 'http://localhost:4873' });\n * registerAllTests(config);\n *\n * Or pick individual suites:\n * import { homeTests, signinTests } from '@verdaccio/e2e-ui';\n *\n * const config = createRegistryConfig({ registryUrl: 'http://localhost:4873' });\n * homeTests(config);\n * signinTests(config);\n */\nexport function registerAllTests(config: RegistryConfig): void {\n homeTests(config);\n signinTests(config);\n layoutTests(config);\n searchTests(config);\n settingsTests(config);\n publishTests(config);\n detailTests(config);\n failureModeTests(config);\n i18nKeyTests(config);\n signupTests(config);\n changePasswordTests(config);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,SAAgB,qBAAqB,SAA6C;CAChF,MAAM,MAAM,IAAI,IAAI,QAAQ,WAAW;CACvC,OAAO;EACL,aAAa,QAAQ;EACrB,MAAM,QAAQ,SAAS,SAAS,IAAI,MAAM,EAAE,KAAK;EACjD,aAAa,QAAQ,eAAe;GAAE,MAAM;GAAQ,UAAU;EAAO;EACrE,OAAO,QAAQ,SAAS;EACxB,SAAS,gBAAA,aAAa,gBAAA,kBAAkB,QAAQ,OAAO;EACvD,WAAW,gBAAA,eAAe,gBAAA,mBAAmB,QAAQ,SAAS;EAC9D,UAAU,iBAAA,cAAc,iBAAA,kBAAkB,QAAQ,QAAQ;CAC5D;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,oBAAoB,IAA0B,SAAmC;CAC/F,MAAM,SAAS,qBAAqB,OAAO;CAG3C,GAAG,cAAc,YAAY;EAC3B,MAAM,cAAc,QAAQ,IAAI;EAChC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,UAAU,UAAU;EAEtD,MAAM,iBAAiB;EACvB,MAAM,QAAkB,CAAC;EAEzB,MAAM,KAAK,0BAA0B;EACrC,MAAM,KAAK,2DAA2D;EACtE,MAAM,KAAK,2DAA2D;EAEtE,KAAK,MAAM,OAAO,eAAe,MAAM;GACrC,MAAM,WAAW,IAAI,KAAK,YAAY,IAAI,KAAK;GAC/C,MAAM,QAAQ,IAAI;GAClB,MAAM,OAAO,MAAM,WAAW,IAAI,MAAM;GACxC,MAAM,MAAM,MAAM,WAAW,IAAI,MAAM,WAAW,IAAA,CAAM,QAAQ,CAAC,EAAE,KAAK;GACxE,MAAM,KACJ,KAAK,KAAK,GAAG,SAAS,KAAK,MAAM,MAAM,KAAK,MAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,QAAQ,KAAK,IAAI,GAC3G;EACF;EAEA,MAAM,KAAK,EAAE;EACb,MAAM,SAAS,eAAe,cAAc;EAC5C,MAAM,SAAS,eAAe,eAAe;EAC7C,MAAM,SAAS,eAAe,eAAe;EAC7C,MAAM,UAAU,eAAe,gBAAgB;EAC/C,MAAM,QAAQ,SAAS,IAAI,MAAM;EACjC,MAAM,KACJ,GAAG,MAAM,KAAK,OAAO,eAAe,OAAO,eAAe,QAAQ,cAAc,OAAO,UACzF;EAEA,CAAA,GAAA,GAAA,eAAA,CAAe,aAAa,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;CAED,GAAG,QAAQ;EACT,WAA+B;GAC7B,OAAO;IACL,aAAa,OAAO;IACpB,MAAM,OAAO;GACf;EACF;;;;;;;;;;;EAWA,MAAM,eAAe,OAA+D;GAClF,OAAO,gBAAA,eAAe;IACpB,aAAa,MAAM,eAAe,OAAO;IACzC,aAAa,MAAM,eAAe,OAAO;IACzC,SAAS,MAAM;IACf,SAAS,MAAM;IACf,cAAc,MAAM;IACpB,iBAAiB,MAAM;IACvB,QAAQ,MAAM;GAChB,CAAC;EACH;;;;;;;EAOA,MAAM,iBAAiB,YAAmC;GACxD,MAAM,gBAAA,iBAAiB,UAAU;GACjC,OAAO;EACT;;;;;;;;;EASA,MAAM,iBACJ,OAKiC;GACjC,MAAM,aACJ,OAAO,UAAU,WACb;IAAE,SAAS;IAAO,aAAa,OAAO;GAAY,IAClD;IAAE,GAAG;IAAO,aAAa,MAAM,eAAe,OAAO;GAAY;GACvE,OAAO,gBAAA,iBAAiB,UAAU;EACpC;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;AAkBA,SAAgB,iBAAiB,QAA8B;CAC7D,aAAA,UAAU,MAAM;CAChB,eAAA,YAAY,MAAM;CAClB,eAAA,YAAY,MAAM;CAClB,eAAA,YAAY,MAAM;CAClB,iBAAA,cAAc,MAAM;CACpB,kBAAA,aAAa,MAAM;CACnB,eAAA,YAAY,MAAM;CAClB,sBAAA,iBAAiB,MAAM;CACvB,kBAAA,aAAa,MAAM;CACnB,eAAA,YAAY,MAAM;CAClB,wBAAA,oBAAoB,MAAM;AAC5B"}
@@ -54,7 +54,8 @@ var DEFAULT_TEST_IDS = {
54
54
  rawBtn: "raw-btn",
55
55
  rawViewerDialog: "rawViewer--dialog",
56
56
  closeRawViewer: "close-raw-viewer"
57
- }
57
+ },
58
+ errors: { genericError: "generic-error" }
58
59
  };
59
60
  /**
60
61
  * Defaults for non-testid CSS selectors. Overridable via
@@ -66,6 +67,12 @@ var DEFAULT_SELECTORS = {
66
67
  usernameInput: "#login--dialog-username",
67
68
  passwordInput: "#login--dialog-password",
68
69
  submitButton: "#login--dialog-button-submit"
70
+ },
71
+ signup: {
72
+ usernameInput: "#login--dialog-username",
73
+ passwordInput: "#login--dialog-password",
74
+ emailInput: "input[type=\"email\"]",
75
+ submitButton: "button[type=\"submit\"]"
69
76
  }
70
77
  };
71
78
  /**
@@ -97,6 +104,10 @@ function mergeTestIds(defaults, overrides) {
97
104
  package: {
98
105
  ...defaults.package,
99
106
  ...overrides.package
107
+ },
108
+ errors: {
109
+ ...defaults.errors,
110
+ ...overrides.errors
100
111
  }
101
112
  };
102
113
  }
@@ -108,6 +119,10 @@ function mergeSelectors(defaults, overrides) {
108
119
  loginDialog: {
109
120
  ...defaults.loginDialog,
110
121
  ...overrides.loginDialog
122
+ },
123
+ signup: {
124
+ ...defaults.signup,
125
+ ...overrides.signup
111
126
  }
112
127
  };
113
128
  }
@@ -1 +1 @@
1
- {"version":3,"file":"testIds.cjs","names":[],"sources":["../../src/testIds.ts"],"sourcesContent":["/**\n * Configurable DOM selectors used by the e2e-ui test suites.\n *\n * The Verdaccio UI is a moving target — data-testids can and do change\n * between majors — so every selector referenced by a test lives here\n * and can be overridden by consumers of `@verdaccio/e2e-ui` via\n * `createRegistryConfig({ testIds, selectors })`.\n *\n * The defaults below match Verdaccio 6.x as of the last time we audited\n * the ui-components source. If a selector moves, override just the\n * affected field instead of forking the suite.\n */\n\n/**\n * Map of data-testid values used by the test suites, grouped by UI\n * section. Each field holds the bare testid string (no `data-testid=\"…\"`\n * wrapping) — the test helpers pass it through `cy.getByTestId(...)`.\n */\nexport interface TestIds {\n home: {\n /** Help card shown on the empty-registry landing page. */\n helpCard: string;\n /** 404 \"not found\" container. */\n notFound: string;\n };\n header: {\n /** Outermost `<NavBar>` element. */\n container: string;\n /** Inner wrapper inside the nav bar. */\n innerNavBar: string;\n /** Right-side action cluster wrapper. */\n right: string;\n /** Wrapper around the header search input. */\n searchContainer: string;\n /** Default SVG Verdaccio logo. */\n defaultLogo: string;\n /** Custom (user-provided) logo image. */\n customLogo: string;\n /** \"Login\" button shown when logged out. */\n loginButton: string;\n /** Gear icon that opens the settings dialog. */\n settingsTooltip: string;\n /** Info icon that opens the registry info dialog. */\n infoTooltip: string;\n /**\n * Theme switch button shown while in LIGHT mode (clicking it\n * flips to dark). The underlying component swaps between this\n * and `themeSwitchDark` based on `isDarkMode`.\n */\n themeSwitchLight: string;\n /** Theme switch button shown while in DARK mode. */\n themeSwitchDark: string;\n /** Menu icon shown after login (opens the logged-in menu). */\n logInDialogIcon: string;\n /** \"Log out\" entry inside the logged-in menu. */\n logOutDialogIcon: string;\n /** \"Hi <username>\" label inside the logged-in menu. */\n greetingsLabel: string;\n };\n footer: {\n /** Outer footer wrapper. */\n container: string;\n /** \"Powered by\" version text on the right side of the footer. */\n version: string;\n };\n login: {\n /** Login dialog container (the MUI Dialog root). */\n dialog: string;\n /** DialogContent wrapper inside the login dialog. */\n dialogContent: string;\n /**\n * Error banner shown inside the login dialog when the server\n * rejects credentials (or any other `errors.root` message the form\n * sets). Renders inside the `LoginDialogFormError` component.\n */\n error: string;\n };\n package: {\n /** Wrapper around the list of packages on the home page. */\n itemList: string;\n /** Package name link in the package list (home + search results). */\n title: string;\n /** Readme container on the package detail page. */\n readme: string;\n /** Sidebar container on the package detail page. */\n sidebar: string;\n /** Install commands section list. */\n installList: string;\n /** Individual install line for npm. */\n installNpm: string;\n /** Individual install line for yarn. */\n installYarn: string;\n /** Individual install line for pnpm. */\n installPnpm: string;\n /** Keyword list below the install section. */\n keywordList: string;\n /** Tab that reveals the dependencies view. */\n dependenciesTab: string;\n /** Dependencies list wrapper (one entry per dep). */\n dependencies: string;\n /** Tab that reveals the versions view. */\n versionsTab: string;\n /** \"latest\" tag row inside the versions view. */\n tagLatest: string;\n /** Tab that reveals the uplinks view. */\n uplinksTab: string;\n /** Empty-state message when the package has no uplinks. */\n noUplinks: string;\n /** Action-bar tarball download FAB. */\n downloadTarballBtn: string;\n /** Package list tarball download button. */\n downloadTarball: string;\n /** Action-bar \"view raw manifest\" FAB. */\n rawBtn: string;\n /** Full-screen dialog that opens when `rawBtn` is clicked. */\n rawViewerDialog: string;\n /** Close button inside the raw viewer dialog. */\n closeRawViewer: string;\n };\n}\n\n/**\n * CSS selectors (not data-testids) used by the test suites. These are\n * things like form-field IDs and framework-specific class names that\n * Verdaccio's UI exposes as plain selectors rather than testids.\n */\nexport interface Selectors {\n /** Class applied to the parsed README markdown body. */\n markdownBody: string;\n loginDialog: {\n /** Username text input inside the login dialog. */\n usernameInput: string;\n /** Password text input inside the login dialog. */\n passwordInput: string;\n /** Submit button inside the login dialog. */\n submitButton: string;\n };\n}\n\n/**\n * Defaults matching Verdaccio 6.x (bundled ui-theme@9.0.0-next-9.x).\n * Overridable via `createRegistryConfig({ testIds: { ... } })`.\n */\nexport const DEFAULT_TEST_IDS: TestIds = {\n home: {\n helpCard: 'help-card',\n notFound: '404',\n },\n header: {\n container: 'header',\n innerNavBar: 'inner-nav-bar',\n right: 'header-right',\n searchContainer: 'search-container',\n defaultLogo: 'default-logo',\n customLogo: 'custom-logo',\n loginButton: 'header--button-login',\n settingsTooltip: 'header--tooltip-settings',\n infoTooltip: 'header--tooltip-info',\n themeSwitchLight: 'header--button--light',\n themeSwitchDark: 'header--button--dark',\n logInDialogIcon: 'logInDialogIcon',\n logOutDialogIcon: 'logOutDialogIcon',\n greetingsLabel: 'greetings-label',\n },\n footer: {\n container: 'footer',\n version: 'version-footer',\n },\n login: {\n dialog: 'login--dialog',\n dialogContent: 'dialogContentLogin',\n error: 'error',\n },\n package: {\n itemList: 'package-item-list',\n title: 'package-title',\n readme: 'readme',\n sidebar: 'sidebar',\n installList: 'installList',\n installNpm: 'installListItem-npm',\n installYarn: 'installListItem-yarn',\n installPnpm: 'installListItem-pnpm',\n keywordList: 'keyword-list',\n dependenciesTab: 'dependencies-tab',\n dependencies: 'dependencies',\n versionsTab: 'versions-tab',\n tagLatest: 'tag-latest',\n uplinksTab: 'uplinks-tab',\n noUplinks: 'no-uplinks',\n downloadTarballBtn: 'download-tarball-btn',\n downloadTarball: 'download-tarball',\n rawBtn: 'raw-btn',\n rawViewerDialog: 'rawViewer--dialog',\n closeRawViewer: 'close-raw-viewer',\n },\n};\n\n/**\n * Defaults for non-testid CSS selectors. Overridable via\n * `createRegistryConfig({ selectors: { ... } })`.\n */\nexport const DEFAULT_SELECTORS: Selectors = {\n markdownBody: '.markdown-body',\n loginDialog: {\n usernameInput: '#login--dialog-username',\n passwordInput: '#login--dialog-password',\n submitButton: '#login--dialog-button-submit',\n },\n};\n\n/** Deep-partial helper — every field of a nested object becomes optional. */\nexport type DeepPartial<T> = {\n [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];\n};\n\n/**\n * Merge user overrides into the default testIds map. Merging is\n * per-section (one level deep): `overrides.header` replaces individual\n * fields under `defaults.header` without touching `defaults.footer`.\n * The shape is fixed and small, so we enumerate sections by hand\n * rather than relying on a recursive generic merger.\n */\nexport function mergeTestIds(defaults: TestIds, overrides?: DeepPartial<TestIds>): TestIds {\n if (!overrides) return defaults;\n return {\n home: { ...defaults.home, ...overrides.home },\n header: { ...defaults.header, ...overrides.header },\n footer: { ...defaults.footer, ...overrides.footer },\n login: { ...defaults.login, ...overrides.login },\n package: { ...defaults.package, ...overrides.package },\n };\n}\n\n/** Same idea as `mergeTestIds`, for the CSS-selector block. */\nexport function mergeSelectors(defaults: Selectors, overrides?: DeepPartial<Selectors>): Selectors {\n if (!overrides) return defaults;\n return {\n markdownBody: overrides.markdownBody ?? defaults.markdownBody,\n loginDialog: { ...defaults.loginDialog, ...overrides.loginDialog },\n };\n}\n"],"mappings":";;;;;AA+IA,IAAa,mBAA4B;CACvC,MAAM;EACJ,UAAU;EACV,UAAU;CACZ;CACA,QAAQ;EACN,WAAW;EACX,aAAa;EACb,OAAO;EACP,iBAAiB;EACjB,aAAa;EACb,YAAY;EACZ,aAAa;EACb,iBAAiB;EACjB,aAAa;EACb,kBAAkB;EAClB,iBAAiB;EACjB,iBAAiB;EACjB,kBAAkB;EAClB,gBAAgB;CAClB;CACA,QAAQ;EACN,WAAW;EACX,SAAS;CACX;CACA,OAAO;EACL,QAAQ;EACR,eAAe;EACf,OAAO;CACT;CACA,SAAS;EACP,UAAU;EACV,OAAO;EACP,QAAQ;EACR,SAAS;EACT,aAAa;EACb,YAAY;EACZ,aAAa;EACb,aAAa;EACb,aAAa;EACb,iBAAiB;EACjB,cAAc;EACd,aAAa;EACb,WAAW;EACX,YAAY;EACZ,WAAW;EACX,oBAAoB;EACpB,iBAAiB;EACjB,QAAQ;EACR,iBAAiB;EACjB,gBAAgB;CAClB;AACF;;;;;AAMA,IAAa,oBAA+B;CAC1C,cAAc;CACd,aAAa;EACX,eAAe;EACf,eAAe;EACf,cAAc;CAChB;AACF;;;;;;;;AAcA,SAAgB,aAAa,UAAmB,WAA2C;CACzF,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO;EACL,MAAM;GAAE,GAAG,SAAS;GAAM,GAAG,UAAU;EAAK;EAC5C,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,OAAO;GAAE,GAAG,SAAS;GAAO,GAAG,UAAU;EAAM;EAC/C,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,UAAU;EAAQ;CACvD;AACF;;AAGA,SAAgB,eAAe,UAAqB,WAA+C;CACjG,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO;EACL,cAAc,UAAU,gBAAgB,SAAS;EACjD,aAAa;GAAE,GAAG,SAAS;GAAa,GAAG,UAAU;EAAY;CACnE;AACF"}
1
+ {"version":3,"file":"testIds.cjs","names":[],"sources":["../../src/testIds.ts"],"sourcesContent":["/**\n * Configurable DOM selectors used by the e2e-ui test suites.\n *\n * The Verdaccio UI is a moving target — data-testids can and do change\n * between majors — so every selector referenced by a test lives here\n * and can be overridden by consumers of `@verdaccio/e2e-ui` via\n * `createRegistryConfig({ testIds, selectors })`.\n *\n * The defaults below match Verdaccio 6.x as of the last time we audited\n * the ui-components source. If a selector moves, override just the\n * affected field instead of forking the suite.\n */\n\n/**\n * Map of data-testid values used by the test suites, grouped by UI\n * section. Each field holds the bare testid string (no `data-testid=\"…\"`\n * wrapping) — the test helpers pass it through `cy.getByTestId(...)`.\n */\nexport interface TestIds {\n home: {\n /** Help card shown on the empty-registry landing page. */\n helpCard: string;\n /** 404 \"not found\" container. */\n notFound: string;\n };\n header: {\n /** Outermost `<NavBar>` element. */\n container: string;\n /** Inner wrapper inside the nav bar. */\n innerNavBar: string;\n /** Right-side action cluster wrapper. */\n right: string;\n /** Wrapper around the header search input. */\n searchContainer: string;\n /** Default SVG Verdaccio logo. */\n defaultLogo: string;\n /** Custom (user-provided) logo image. */\n customLogo: string;\n /** \"Login\" button shown when logged out. */\n loginButton: string;\n /** Gear icon that opens the settings dialog. */\n settingsTooltip: string;\n /** Info icon that opens the registry info dialog. */\n infoTooltip: string;\n /**\n * Theme switch button shown while in LIGHT mode (clicking it\n * flips to dark). The underlying component swaps between this\n * and `themeSwitchDark` based on `isDarkMode`.\n */\n themeSwitchLight: string;\n /** Theme switch button shown while in DARK mode. */\n themeSwitchDark: string;\n /** Menu icon shown after login (opens the logged-in menu). */\n logInDialogIcon: string;\n /** \"Log out\" entry inside the logged-in menu. */\n logOutDialogIcon: string;\n /** \"Hi <username>\" label inside the logged-in menu. */\n greetingsLabel: string;\n };\n footer: {\n /** Outer footer wrapper. */\n container: string;\n /** \"Powered by\" version text on the right side of the footer. */\n version: string;\n };\n login: {\n /** Login dialog container (the MUI Dialog root). */\n dialog: string;\n /** DialogContent wrapper inside the login dialog. */\n dialogContent: string;\n /**\n * Error banner shown inside the login dialog when the server\n * rejects credentials (or any other `errors.root` message the form\n * sets). Renders inside the `LoginDialogFormError` component.\n */\n error: string;\n };\n package: {\n /** Wrapper around the list of packages on the home page. */\n itemList: string;\n /** Package name link in the package list (home + search results). */\n title: string;\n /** Readme container on the package detail page. */\n readme: string;\n /** Sidebar container on the package detail page. */\n sidebar: string;\n /** Install commands section list. */\n installList: string;\n /** Individual install line for npm. */\n installNpm: string;\n /** Individual install line for yarn. */\n installYarn: string;\n /** Individual install line for pnpm. */\n installPnpm: string;\n /** Keyword list below the install section. */\n keywordList: string;\n /** Tab that reveals the dependencies view. */\n dependenciesTab: string;\n /** Dependencies list wrapper (one entry per dep). */\n dependencies: string;\n /** Tab that reveals the versions view. */\n versionsTab: string;\n /** \"latest\" tag row inside the versions view. */\n tagLatest: string;\n /** Tab that reveals the uplinks view. */\n uplinksTab: string;\n /** Empty-state message when the package has no uplinks. */\n noUplinks: string;\n /** Action-bar tarball download FAB. */\n downloadTarballBtn: string;\n /** Package list tarball download button. */\n downloadTarball: string;\n /** Action-bar \"view raw manifest\" FAB. */\n rawBtn: string;\n /** Full-screen dialog that opens when `rawBtn` is clicked. */\n rawViewerDialog: string;\n /** Close button inside the raw viewer dialog. */\n closeRawViewer: string;\n };\n errors: {\n /**\n * Generic error page shown when a data request fails without a\n * specific status (5xx, network failure). Added by\n * verdaccio/verdaccio#6210 — only present on builds with that fix.\n */\n genericError: string;\n };\n}\n\n/**\n * CSS selectors (not data-testids) used by the test suites. These are\n * things like form-field IDs and framework-specific class names that\n * Verdaccio's UI exposes as plain selectors rather than testids.\n */\nexport interface Selectors {\n /** Class applied to the parsed README markdown body. */\n markdownBody: string;\n loginDialog: {\n /** Username text input inside the login dialog. */\n usernameInput: string;\n /** Password text input inside the login dialog. */\n passwordInput: string;\n /** Submit button inside the login dialog. */\n submitButton: string;\n };\n signup: {\n /** Username input on /-/web/add-user (shares ids with the login form). */\n usernameInput: string;\n /** Password input on /-/web/add-user. */\n passwordInput: string;\n /** Email input on /-/web/add-user (only email-typed input on the page). */\n emailInput: string;\n /** Submit button on /-/web/add-user. */\n submitButton: string;\n };\n}\n\n/**\n * Defaults matching Verdaccio 6.x (bundled ui-theme@9.0.0-next-9.x).\n * Overridable via `createRegistryConfig({ testIds: { ... } })`.\n */\nexport const DEFAULT_TEST_IDS: TestIds = {\n home: {\n helpCard: 'help-card',\n notFound: '404',\n },\n header: {\n container: 'header',\n innerNavBar: 'inner-nav-bar',\n right: 'header-right',\n searchContainer: 'search-container',\n defaultLogo: 'default-logo',\n customLogo: 'custom-logo',\n loginButton: 'header--button-login',\n settingsTooltip: 'header--tooltip-settings',\n infoTooltip: 'header--tooltip-info',\n themeSwitchLight: 'header--button--light',\n themeSwitchDark: 'header--button--dark',\n logInDialogIcon: 'logInDialogIcon',\n logOutDialogIcon: 'logOutDialogIcon',\n greetingsLabel: 'greetings-label',\n },\n footer: {\n container: 'footer',\n version: 'version-footer',\n },\n login: {\n dialog: 'login--dialog',\n dialogContent: 'dialogContentLogin',\n error: 'error',\n },\n package: {\n itemList: 'package-item-list',\n title: 'package-title',\n readme: 'readme',\n sidebar: 'sidebar',\n installList: 'installList',\n installNpm: 'installListItem-npm',\n installYarn: 'installListItem-yarn',\n installPnpm: 'installListItem-pnpm',\n keywordList: 'keyword-list',\n dependenciesTab: 'dependencies-tab',\n dependencies: 'dependencies',\n versionsTab: 'versions-tab',\n tagLatest: 'tag-latest',\n uplinksTab: 'uplinks-tab',\n noUplinks: 'no-uplinks',\n downloadTarballBtn: 'download-tarball-btn',\n downloadTarball: 'download-tarball',\n rawBtn: 'raw-btn',\n rawViewerDialog: 'rawViewer--dialog',\n closeRawViewer: 'close-raw-viewer',\n },\n errors: {\n genericError: 'generic-error',\n },\n};\n\n/**\n * Defaults for non-testid CSS selectors. Overridable via\n * `createRegistryConfig({ selectors: { ... } })`.\n */\nexport const DEFAULT_SELECTORS: Selectors = {\n markdownBody: '.markdown-body',\n loginDialog: {\n usernameInput: '#login--dialog-username',\n passwordInput: '#login--dialog-password',\n submitButton: '#login--dialog-button-submit',\n },\n signup: {\n usernameInput: '#login--dialog-username',\n passwordInput: '#login--dialog-password',\n emailInput: 'input[type=\"email\"]',\n submitButton: 'button[type=\"submit\"]',\n },\n};\n\n/** Deep-partial helper — every field of a nested object becomes optional. */\nexport type DeepPartial<T> = {\n [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];\n};\n\n/**\n * Merge user overrides into the default testIds map. Merging is\n * per-section (one level deep): `overrides.header` replaces individual\n * fields under `defaults.header` without touching `defaults.footer`.\n * The shape is fixed and small, so we enumerate sections by hand\n * rather than relying on a recursive generic merger.\n */\nexport function mergeTestIds(defaults: TestIds, overrides?: DeepPartial<TestIds>): TestIds {\n if (!overrides) return defaults;\n return {\n home: { ...defaults.home, ...overrides.home },\n header: { ...defaults.header, ...overrides.header },\n footer: { ...defaults.footer, ...overrides.footer },\n login: { ...defaults.login, ...overrides.login },\n package: { ...defaults.package, ...overrides.package },\n errors: { ...defaults.errors, ...overrides.errors },\n };\n}\n\n/** Same idea as `mergeTestIds`, for the CSS-selector block. */\nexport function mergeSelectors(defaults: Selectors, overrides?: DeepPartial<Selectors>): Selectors {\n if (!overrides) return defaults;\n return {\n markdownBody: overrides.markdownBody ?? defaults.markdownBody,\n loginDialog: { ...defaults.loginDialog, ...overrides.loginDialog },\n signup: { ...defaults.signup, ...overrides.signup },\n };\n}\n"],"mappings":";;;;;AAiKA,IAAa,mBAA4B;CACvC,MAAM;EACJ,UAAU;EACV,UAAU;CACZ;CACA,QAAQ;EACN,WAAW;EACX,aAAa;EACb,OAAO;EACP,iBAAiB;EACjB,aAAa;EACb,YAAY;EACZ,aAAa;EACb,iBAAiB;EACjB,aAAa;EACb,kBAAkB;EAClB,iBAAiB;EACjB,iBAAiB;EACjB,kBAAkB;EAClB,gBAAgB;CAClB;CACA,QAAQ;EACN,WAAW;EACX,SAAS;CACX;CACA,OAAO;EACL,QAAQ;EACR,eAAe;EACf,OAAO;CACT;CACA,SAAS;EACP,UAAU;EACV,OAAO;EACP,QAAQ;EACR,SAAS;EACT,aAAa;EACb,YAAY;EACZ,aAAa;EACb,aAAa;EACb,aAAa;EACb,iBAAiB;EACjB,cAAc;EACd,aAAa;EACb,WAAW;EACX,YAAY;EACZ,WAAW;EACX,oBAAoB;EACpB,iBAAiB;EACjB,QAAQ;EACR,iBAAiB;EACjB,gBAAgB;CAClB;CACA,QAAQ,EACN,cAAc,gBAChB;AACF;;;;;AAMA,IAAa,oBAA+B;CAC1C,cAAc;CACd,aAAa;EACX,eAAe;EACf,eAAe;EACf,cAAc;CAChB;CACA,QAAQ;EACN,eAAe;EACf,eAAe;EACf,YAAY;EACZ,cAAc;CAChB;AACF;;;;;;;;AAcA,SAAgB,aAAa,UAAmB,WAA2C;CACzF,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO;EACL,MAAM;GAAE,GAAG,SAAS;GAAM,GAAG,UAAU;EAAK;EAC5C,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;EAClD,OAAO;GAAE,GAAG,SAAS;GAAO,GAAG,UAAU;EAAM;EAC/C,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,UAAU;EAAQ;EACrD,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;CACpD;AACF;;AAGA,SAAgB,eAAe,UAAqB,WAA+C;CACjG,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO;EACL,cAAc,UAAU,gBAAgB,SAAS;EACjD,aAAa;GAAE,GAAG,SAAS;GAAa,GAAG,UAAU;EAAY;EACjE,QAAQ;GAAE,GAAG,SAAS;GAAQ,GAAG,UAAU;EAAO;CACpD;AACF"}
@@ -0,0 +1,73 @@
1
+ const require_features = require("../features.cjs");
2
+ //#region src/tests/detail.ts
3
+ /**
4
+ * Package detail page tests: version-pinned URLs and the wire format of
5
+ * the UI's data requests.
6
+ *
7
+ * The wire format is an observable contract, not an implementation
8
+ * detail: proxies, intercepts and every npm client expect scoped names
9
+ * as literal `/@scope/name`. A ui-components refactor that
10
+ * percent-encoded the `@` broke exactly these suites
11
+ * (verdaccio/verdaccio#6210), which is why it is pinned here on purpose.
12
+ */
13
+ function detailTests(config) {
14
+ const { features } = config;
15
+ const { package: pkg, home } = config.testIds;
16
+ describe("package detail", () => {
17
+ describe("with a published package", () => {
18
+ const pkgName = "@verdaccio/detail-fixture";
19
+ let tempFolder = null;
20
+ let version = "1.0.0";
21
+ beforeEach(() => {
22
+ cy.task("publishPackage", {
23
+ pkgName,
24
+ version: "1.0.0",
25
+ unique: true
26
+ }).then((result) => {
27
+ tempFolder = result?.tempFolder ?? null;
28
+ version = result?.version ?? "1.0.0";
29
+ });
30
+ });
31
+ afterEach(() => {
32
+ cy.task("unpublishPackage", {
33
+ pkgName,
34
+ tempFolder: tempFolder ?? void 0
35
+ });
36
+ if (tempFolder) cy.task("cleanupPublished", tempFolder);
37
+ tempFolder = null;
38
+ });
39
+ require_features.maybeIt(features.detail.scopedWireFormat)("should request sidebar and readme with the scope literal (@, not %40)", () => {
40
+ cy.intercept("GET", `/-/verdaccio/data/sidebar/${pkgName}*`).as("sidebar");
41
+ cy.intercept("GET", `/-/verdaccio/data/package/readme/${pkgName}*`).as("readme");
42
+ cy.visit(`${config.registryUrl}/-/web/detail/${pkgName}`);
43
+ cy.wait("@sidebar", { timeout: 1e4 }).then((interception) => {
44
+ expect(interception.request.url).to.contain(`/sidebar/${pkgName}`);
45
+ expect(interception.request.url).to.not.contain("%40");
46
+ });
47
+ cy.wait("@readme", { timeout: 1e4 });
48
+ cy.getByTestId(pkg.sidebar).should("be.visible");
49
+ });
50
+ require_features.maybeIt(features.detail.versionPinned)("should pin the requested version via the ?v= query on /v/<version> urls", () => {
51
+ cy.intercept("GET", `/-/verdaccio/data/sidebar/${pkgName}*`).as("sidebar");
52
+ cy.visit(`${config.registryUrl}/-/web/detail/${pkgName}/v/${version}`);
53
+ cy.wait("@sidebar", { timeout: 1e4 }).then((interception) => {
54
+ expect(interception.request.url).to.contain(`v=${version}`);
55
+ });
56
+ cy.getByTestId(pkg.sidebar).should("be.visible");
57
+ cy.getByTestId(pkg.sidebar).contains(version);
58
+ });
59
+ require_features.maybeIt(features.detail.versionNotFound)("should render Not Found for a version that does not exist", () => {
60
+ cy.intercept("GET", `/-/verdaccio/data/sidebar/${pkgName}*`).as("sidebar");
61
+ cy.visit(`${config.registryUrl}/-/web/detail/${pkgName}/v/9.9.9`);
62
+ cy.wait("@sidebar", { timeout: 1e4 }).then((interception) => {
63
+ expect(interception.response?.statusCode).to.eq(404);
64
+ });
65
+ cy.getByTestId(home.notFound, { timeout: 1e4 }).should("be.visible");
66
+ });
67
+ });
68
+ });
69
+ }
70
+ //#endregion
71
+ exports.detailTests = detailTests;
72
+
73
+ //# sourceMappingURL=detail.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail.cjs","names":[],"sources":["../../../src/tests/detail.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\nimport { maybeIt } from '../features';\nimport { RegistryConfig } from '../types';\n\n/**\n * Package detail page tests: version-pinned URLs and the wire format of\n * the UI's data requests.\n *\n * The wire format is an observable contract, not an implementation\n * detail: proxies, intercepts and every npm client expect scoped names\n * as literal `/@scope/name`. A ui-components refactor that\n * percent-encoded the `@` broke exactly these suites\n * (verdaccio/verdaccio#6210), which is why it is pinned here on purpose.\n */\nexport function detailTests(config: RegistryConfig) {\n const { features } = config;\n const { package: pkg, home } = config.testIds;\n\n describe('package detail', () => {\n describe('with a published package', () => {\n const pkgName = '@verdaccio/detail-fixture';\n let tempFolder: string | null = null;\n // `unique: true` uniquifies the VERSION (1.0.0-t<ts>), not the name\n let version = '1.0.0';\n\n beforeEach(() => {\n cy.task('publishPackage', {\n pkgName,\n version: '1.0.0',\n unique: true,\n }).then((result) => {\n tempFolder = result?.tempFolder ?? null;\n version = result?.version ?? '1.0.0';\n });\n });\n\n afterEach(() => {\n cy.task('unpublishPackage', {\n pkgName,\n tempFolder: tempFolder ?? undefined,\n });\n if (tempFolder) {\n cy.task('cleanupPublished', tempFolder);\n }\n tempFolder = null;\n });\n\n maybeIt(features.detail.scopedWireFormat)(\n 'should request sidebar and readme with the scope literal (@, not %40)',\n () => {\n // Literal-path intercepts: if the UI ever encodes `@` or `/`,\n // these never match and the waits below time out.\n cy.intercept('GET', `/-/verdaccio/data/sidebar/${pkgName}*`).as('sidebar');\n cy.intercept('GET', `/-/verdaccio/data/package/readme/${pkgName}*`).as('readme');\n\n cy.visit(`${config.registryUrl}/-/web/detail/${pkgName}`);\n\n cy.wait('@sidebar', { timeout: 10000 }).then((interception: any) => {\n expect(interception.request.url).to.contain(`/sidebar/${pkgName}`);\n expect(interception.request.url).to.not.contain('%40');\n });\n cy.wait('@readme', { timeout: 10000 });\n cy.getByTestId(pkg.sidebar).should('be.visible');\n }\n );\n\n maybeIt(features.detail.versionPinned)(\n 'should pin the requested version via the ?v= query on /v/<version> urls',\n () => {\n cy.intercept('GET', `/-/verdaccio/data/sidebar/${pkgName}*`).as('sidebar');\n\n cy.visit(`${config.registryUrl}/-/web/detail/${pkgName}/v/${version}`);\n\n cy.wait('@sidebar', { timeout: 10000 }).then((interception: any) => {\n expect(interception.request.url).to.contain(`v=${version}`);\n });\n cy.getByTestId(pkg.sidebar).should('be.visible');\n cy.getByTestId(pkg.sidebar).contains(version);\n }\n );\n\n maybeIt(features.detail.versionNotFound)(\n 'should render Not Found for a version that does not exist',\n () => {\n cy.intercept('GET', `/-/verdaccio/data/sidebar/${pkgName}*`).as('sidebar');\n\n cy.visit(`${config.registryUrl}/-/web/detail/${pkgName}/v/9.9.9`);\n\n // The server answers 404 for unknown versions instead of\n // silently serving `latest` under the requested title.\n cy.wait('@sidebar', { timeout: 10000 }).then((interception: any) => {\n expect(interception.response?.statusCode).to.eq(404);\n });\n cy.getByTestId(home.notFound, { timeout: 10000 }).should('be.visible');\n }\n );\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;AAcA,SAAgB,YAAY,QAAwB;CAClD,MAAM,EAAE,aAAa;CACrB,MAAM,EAAE,SAAS,KAAK,SAAS,OAAO;CAEtC,SAAS,wBAAwB;EAC/B,SAAS,kCAAkC;GACzC,MAAM,UAAU;GAChB,IAAI,aAA4B;GAEhC,IAAI,UAAU;GAEd,iBAAiB;IACf,GAAG,KAAK,kBAAkB;KACxB;KACA,SAAS;KACT,QAAQ;IACV,CAAC,CAAC,CAAC,MAAM,WAAW;KAClB,aAAa,QAAQ,cAAc;KACnC,UAAU,QAAQ,WAAW;IAC/B,CAAC;GACH,CAAC;GAED,gBAAgB;IACd,GAAG,KAAK,oBAAoB;KAC1B;KACA,YAAY,cAAc,KAAA;IAC5B,CAAC;IACD,IAAI,YACF,GAAG,KAAK,oBAAoB,UAAU;IAExC,aAAa;GACf,CAAC;GAED,iBAAA,QAAQ,SAAS,OAAO,gBAAgB,CAAC,CACvC,+EACM;IAGJ,GAAG,UAAU,OAAO,6BAA6B,QAAQ,EAAE,CAAC,CAAC,GAAG,SAAS;IACzE,GAAG,UAAU,OAAO,oCAAoC,QAAQ,EAAE,CAAC,CAAC,GAAG,QAAQ;IAE/E,GAAG,MAAM,GAAG,OAAO,YAAY,gBAAgB,SAAS;IAExD,GAAG,KAAK,YAAY,EAAE,SAAS,IAAM,CAAC,CAAC,CAAC,MAAM,iBAAsB;KAClE,OAAO,aAAa,QAAQ,GAAG,CAAC,CAAC,GAAG,QAAQ,YAAY,SAAS;KACjE,OAAO,aAAa,QAAQ,GAAG,CAAC,CAAC,GAAG,IAAI,QAAQ,KAAK;IACvD,CAAC;IACD,GAAG,KAAK,WAAW,EAAE,SAAS,IAAM,CAAC;IACrC,GAAG,YAAY,IAAI,OAAO,CAAC,CAAC,OAAO,YAAY;GACjD,CACF;GAEA,iBAAA,QAAQ,SAAS,OAAO,aAAa,CAAC,CACpC,iFACM;IACJ,GAAG,UAAU,OAAO,6BAA6B,QAAQ,EAAE,CAAC,CAAC,GAAG,SAAS;IAEzE,GAAG,MAAM,GAAG,OAAO,YAAY,gBAAgB,QAAQ,KAAK,SAAS;IAErE,GAAG,KAAK,YAAY,EAAE,SAAS,IAAM,CAAC,CAAC,CAAC,MAAM,iBAAsB;KAClE,OAAO,aAAa,QAAQ,GAAG,CAAC,CAAC,GAAG,QAAQ,KAAK,SAAS;IAC5D,CAAC;IACD,GAAG,YAAY,IAAI,OAAO,CAAC,CAAC,OAAO,YAAY;IAC/C,GAAG,YAAY,IAAI,OAAO,CAAC,CAAC,SAAS,OAAO;GAC9C,CACF;GAEA,iBAAA,QAAQ,SAAS,OAAO,eAAe,CAAC,CACtC,mEACM;IACJ,GAAG,UAAU,OAAO,6BAA6B,QAAQ,EAAE,CAAC,CAAC,GAAG,SAAS;IAEzE,GAAG,MAAM,GAAG,OAAO,YAAY,gBAAgB,QAAQ,SAAS;IAIhE,GAAG,KAAK,YAAY,EAAE,SAAS,IAAM,CAAC,CAAC,CAAC,MAAM,iBAAsB;KAClE,OAAO,aAAa,UAAU,UAAU,CAAC,CAAC,GAAG,GAAG,GAAG;IACrD,CAAC;IACD,GAAG,YAAY,KAAK,UAAU,EAAE,SAAS,IAAM,CAAC,CAAC,CAAC,OAAO,YAAY;GACvE,CACF;EACF,CAAC;CACH,CAAC;AACH"}
@@ -0,0 +1,75 @@
1
+ const require_features = require("../features.cjs");
2
+ //#region src/tests/failure-modes.ts
3
+ /**
4
+ * Failure-mode tests: what the UI shows when the backend misbehaves.
5
+ *
6
+ * A registry that is down must never look like a healthy empty registry
7
+ * (that invites the user to publish into the void), and a failed detail
8
+ * request must render an error state instead of a blank page. These are
9
+ * the failure classes fixed by verdaccio/verdaccio#6210 — the strict
10
+ * assertions are feature-gated probes until that ships.
11
+ */
12
+ function failureModeTests(config) {
13
+ const { features } = config;
14
+ const { home, header, errors, package: pkg } = config.testIds;
15
+ describe("failure modes", () => {
16
+ require_features.maybeIt(features.failureModes.homeServerError)("a 5xx on the package list must not render the empty-registry onboarding", () => {
17
+ cy.intercept("GET", "/-/verdaccio/data/packages", {
18
+ statusCode: 500,
19
+ body: { error: "internal server error" }
20
+ }).as("pkgs");
21
+ cy.visit(config.registryUrl);
22
+ cy.wait("@pkgs", { timeout: 1e4 });
23
+ cy.getByTestId(header.container).should("be.visible");
24
+ cy.getByTestId(home.helpCard).should("not.exist");
25
+ });
26
+ require_features.maybeIt(features.failureModes.homeNetworkError)("an unreachable backend must not render the empty-registry onboarding", () => {
27
+ cy.intercept("GET", "/-/verdaccio/data/packages", { forceNetworkError: true }).as("pkgs");
28
+ cy.visit(config.registryUrl);
29
+ cy.wait("@pkgs", { timeout: 1e4 });
30
+ cy.getByTestId(header.container).should("be.visible");
31
+ cy.getByTestId(home.helpCard).should("not.exist");
32
+ cy.getByTestId(errors.genericError, { timeout: 1e4 }).should("be.visible");
33
+ });
34
+ describe("with a published package", () => {
35
+ const pkgName = "@verdaccio/failure-fixture";
36
+ let tempFolder = null;
37
+ beforeEach(() => {
38
+ cy.task("publishPackage", {
39
+ pkgName,
40
+ version: "1.0.0",
41
+ unique: true
42
+ }).then((result) => {
43
+ tempFolder = result?.tempFolder ?? null;
44
+ });
45
+ });
46
+ afterEach(() => {
47
+ cy.task("unpublishPackage", {
48
+ pkgName,
49
+ tempFolder: tempFolder ?? void 0
50
+ });
51
+ if (tempFolder) cy.task("cleanupPublished", tempFolder);
52
+ tempFolder = null;
53
+ });
54
+ require_features.maybeIt(features.failureModes.detailErrorState)("a 5xx on the detail data must render an error state, not a blank page", () => {
55
+ cy.intercept("GET", `/-/verdaccio/data/sidebar/${pkgName}*`, {
56
+ statusCode: 500,
57
+ body: { error: "internal server error" }
58
+ }).as("sidebar");
59
+ cy.intercept("GET", `/-/verdaccio/data/package/readme/${pkgName}*`, {
60
+ statusCode: 500,
61
+ body: { error: "internal server error" }
62
+ }).as("readme");
63
+ cy.visit(`${config.registryUrl}/-/web/detail/${pkgName}`);
64
+ cy.wait("@sidebar", { timeout: 1e4 });
65
+ cy.getByTestId(errors.genericError, { timeout: 1e4 }).should("be.visible");
66
+ cy.getByTestId(header.container).should("be.visible");
67
+ cy.getByTestId(pkg.sidebar).should("not.exist");
68
+ });
69
+ });
70
+ });
71
+ }
72
+ //#endregion
73
+ exports.failureModeTests = failureModeTests;
74
+
75
+ //# sourceMappingURL=failure-modes.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure-modes.cjs","names":[],"sources":["../../../src/tests/failure-modes.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\nimport { maybeIt } from '../features';\nimport { RegistryConfig } from '../types';\n\n/**\n * Failure-mode tests: what the UI shows when the backend misbehaves.\n *\n * A registry that is down must never look like a healthy empty registry\n * (that invites the user to publish into the void), and a failed detail\n * request must render an error state instead of a blank page. These are\n * the failure classes fixed by verdaccio/verdaccio#6210 — the strict\n * assertions are feature-gated probes until that ships.\n */\nexport function failureModeTests(config: RegistryConfig) {\n const { features } = config;\n const { home, header, errors, package: pkg } = config.testIds;\n\n describe('failure modes', () => {\n maybeIt(features.failureModes.homeServerError)(\n 'a 5xx on the package list must not render the empty-registry onboarding',\n () => {\n cy.intercept('GET', '/-/verdaccio/data/packages', {\n statusCode: 500,\n body: { error: 'internal server error' },\n }).as('pkgs');\n\n cy.visit(config.registryUrl);\n cy.wait('@pkgs', { timeout: 10000 });\n\n // The header must survive and the onboarding card must not\n // appear — a dead backend is not an empty registry.\n cy.getByTestId(header.container).should('be.visible');\n cy.getByTestId(home.helpCard).should('not.exist');\n }\n );\n\n maybeIt(features.failureModes.homeNetworkError)(\n 'an unreachable backend must not render the empty-registry onboarding',\n () => {\n cy.intercept('GET', '/-/verdaccio/data/packages', { forceNetworkError: true }).as('pkgs');\n\n cy.visit(config.registryUrl);\n cy.wait('@pkgs', { timeout: 10000 });\n\n cy.getByTestId(header.container).should('be.visible');\n cy.getByTestId(home.helpCard).should('not.exist');\n cy.getByTestId(errors.genericError, { timeout: 10000 }).should('be.visible');\n }\n );\n\n describe('with a published package', () => {\n const pkgName = '@verdaccio/failure-fixture';\n let tempFolder: string | null = null;\n\n beforeEach(() => {\n cy.task('publishPackage', {\n pkgName,\n version: '1.0.0',\n unique: true,\n }).then((result) => {\n tempFolder = result?.tempFolder ?? null;\n });\n });\n\n afterEach(() => {\n cy.task('unpublishPackage', {\n pkgName,\n tempFolder: tempFolder ?? undefined,\n });\n if (tempFolder) {\n cy.task('cleanupPublished', tempFolder);\n }\n tempFolder = null;\n });\n\n maybeIt(features.failureModes.detailErrorState)(\n 'a 5xx on the detail data must render an error state, not a blank page',\n () => {\n cy.intercept('GET', `/-/verdaccio/data/sidebar/${pkgName}*`, {\n statusCode: 500,\n body: { error: 'internal server error' },\n }).as('sidebar');\n cy.intercept('GET', `/-/verdaccio/data/package/readme/${pkgName}*`, {\n statusCode: 500,\n body: { error: 'internal server error' },\n }).as('readme');\n\n cy.visit(`${config.registryUrl}/-/web/detail/${pkgName}`);\n cy.wait('@sidebar', { timeout: 10000 });\n\n // Error page instead of a blank layout whose tabs crash the\n // whole app; the header must survive.\n cy.getByTestId(errors.genericError, { timeout: 10000 }).should('be.visible');\n cy.getByTestId(header.container).should('be.visible');\n cy.getByTestId(pkg.sidebar).should('not.exist');\n }\n );\n });\n });\n}\n"],"mappings":";;;;;;;;;;;AAaA,SAAgB,iBAAiB,QAAwB;CACvD,MAAM,EAAE,aAAa;CACrB,MAAM,EAAE,MAAM,QAAQ,QAAQ,SAAS,QAAQ,OAAO;CAEtD,SAAS,uBAAuB;EAC9B,iBAAA,QAAQ,SAAS,aAAa,eAAe,CAAC,CAC5C,iFACM;GACJ,GAAG,UAAU,OAAO,8BAA8B;IAChD,YAAY;IACZ,MAAM,EAAE,OAAO,wBAAwB;GACzC,CAAC,CAAC,CAAC,GAAG,MAAM;GAEZ,GAAG,MAAM,OAAO,WAAW;GAC3B,GAAG,KAAK,SAAS,EAAE,SAAS,IAAM,CAAC;GAInC,GAAG,YAAY,OAAO,SAAS,CAAC,CAAC,OAAO,YAAY;GACpD,GAAG,YAAY,KAAK,QAAQ,CAAC,CAAC,OAAO,WAAW;EAClD,CACF;EAEA,iBAAA,QAAQ,SAAS,aAAa,gBAAgB,CAAC,CAC7C,8EACM;GACJ,GAAG,UAAU,OAAO,8BAA8B,EAAE,mBAAmB,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM;GAExF,GAAG,MAAM,OAAO,WAAW;GAC3B,GAAG,KAAK,SAAS,EAAE,SAAS,IAAM,CAAC;GAEnC,GAAG,YAAY,OAAO,SAAS,CAAC,CAAC,OAAO,YAAY;GACpD,GAAG,YAAY,KAAK,QAAQ,CAAC,CAAC,OAAO,WAAW;GAChD,GAAG,YAAY,OAAO,cAAc,EAAE,SAAS,IAAM,CAAC,CAAC,CAAC,OAAO,YAAY;EAC7E,CACF;EAEA,SAAS,kCAAkC;GACzC,MAAM,UAAU;GAChB,IAAI,aAA4B;GAEhC,iBAAiB;IACf,GAAG,KAAK,kBAAkB;KACxB;KACA,SAAS;KACT,QAAQ;IACV,CAAC,CAAC,CAAC,MAAM,WAAW;KAClB,aAAa,QAAQ,cAAc;IACrC,CAAC;GACH,CAAC;GAED,gBAAgB;IACd,GAAG,KAAK,oBAAoB;KAC1B;KACA,YAAY,cAAc,KAAA;IAC5B,CAAC;IACD,IAAI,YACF,GAAG,KAAK,oBAAoB,UAAU;IAExC,aAAa;GACf,CAAC;GAED,iBAAA,QAAQ,SAAS,aAAa,gBAAgB,CAAC,CAC7C,+EACM;IACJ,GAAG,UAAU,OAAO,6BAA6B,QAAQ,IAAI;KAC3D,YAAY;KACZ,MAAM,EAAE,OAAO,wBAAwB;IACzC,CAAC,CAAC,CAAC,GAAG,SAAS;IACf,GAAG,UAAU,OAAO,oCAAoC,QAAQ,IAAI;KAClE,YAAY;KACZ,MAAM,EAAE,OAAO,wBAAwB;IACzC,CAAC,CAAC,CAAC,GAAG,QAAQ;IAEd,GAAG,MAAM,GAAG,OAAO,YAAY,gBAAgB,SAAS;IACxD,GAAG,KAAK,YAAY,EAAE,SAAS,IAAM,CAAC;IAItC,GAAG,YAAY,OAAO,cAAc,EAAE,SAAS,IAAM,CAAC,CAAC,CAAC,OAAO,YAAY;IAC3E,GAAG,YAAY,OAAO,SAAS,CAAC,CAAC,OAAO,YAAY;IACpD,GAAG,YAAY,IAAI,OAAO,CAAC,CAAC,OAAO,WAAW;GAChD,CACF;EACF,CAAC;CACH,CAAC;AACH"}
@@ -0,0 +1,74 @@
1
+ const require_features = require("../features.cjs");
2
+ //#region src/tests/i18n-keys.ts
3
+ /**
4
+ * Raw-i18n-key detector: no page may render untranslated keys like
5
+ * `security.addUser.title` as visible text.
6
+ *
7
+ * This pins the whole "namespace missing from the shipped bundle" class
8
+ * (the `security.*` namespace was absent from every published ui-theme
9
+ * until verdaccio/verdaccio#6210): a missing key makes i18next return
10
+ * the key itself, which this scan catches regardless of which key
11
+ * regressed.
12
+ */
13
+ /**
14
+ * Namespaces of the ui-theme translation file. Restricting the scan to
15
+ * known prefixes keeps false positives out (package names like
16
+ * `lodash.merge` or readme prose with dots must not trip it).
17
+ */
18
+ var I18N_NAMESPACES = [
19
+ "security",
20
+ "dialog",
21
+ "header",
22
+ "sidebar",
23
+ "package",
24
+ "versions",
25
+ "search",
26
+ "form",
27
+ "form-placeholder",
28
+ "form-validation",
29
+ "button",
30
+ "error",
31
+ "uplinks",
32
+ "dependencies",
33
+ "autoComplete",
34
+ "footer",
35
+ "stage",
36
+ "help"
37
+ ].join("|");
38
+ var RAW_KEY_PATTERN = new RegExp(`(?:^|[\\s>])(?:${I18N_NAMESPACES})\\.[a-zA-Z][a-zA-Z.-]+`);
39
+ function assertNoRawI18nKeys(context) {
40
+ cy.get("body").invoke("text").then((text) => {
41
+ const match = text.match(RAW_KEY_PATTERN);
42
+ expect(match, `raw i18n key rendered on ${context}: ${match?.[0] ?? ""}`).to.be.null;
43
+ });
44
+ }
45
+ function i18nKeyTests(config) {
46
+ const { features } = config;
47
+ const { header } = config.testIds;
48
+ describe("i18n keys", () => {
49
+ require_features.maybeIt(features.i18n.noRawKeysCorePages)("home page must not render raw i18n keys", () => {
50
+ cy.visit(config.registryUrl);
51
+ cy.getByTestId(header.container).should("be.visible");
52
+ assertNoRawI18nKeys("home");
53
+ });
54
+ require_features.maybeIt(features.i18n.noRawKeysSecurityPages)("login dialog and security pages must not render raw i18n keys", () => {
55
+ cy.visit(config.registryUrl);
56
+ cy.getByTestId(config.testIds.header.loginButton).click();
57
+ cy.getByTestId(config.testIds.login.dialogContent).should("be.visible");
58
+ assertNoRawI18nKeys("login dialog");
59
+ for (const path of [
60
+ "/-/web/login",
61
+ "/-/web/add-user",
62
+ "/-/web/change-password"
63
+ ]) {
64
+ cy.visit(`${config.registryUrl}${path}`, { failOnStatusCode: false });
65
+ cy.get("body").should("be.visible");
66
+ assertNoRawI18nKeys(path);
67
+ }
68
+ });
69
+ });
70
+ }
71
+ //#endregion
72
+ exports.i18nKeyTests = i18nKeyTests;
73
+
74
+ //# sourceMappingURL=i18n-keys.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n-keys.cjs","names":[],"sources":["../../../src/tests/i18n-keys.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\nimport { maybeIt } from '../features';\nimport { RegistryConfig } from '../types';\n\n/**\n * Raw-i18n-key detector: no page may render untranslated keys like\n * `security.addUser.title` as visible text.\n *\n * This pins the whole \"namespace missing from the shipped bundle\" class\n * (the `security.*` namespace was absent from every published ui-theme\n * until verdaccio/verdaccio#6210): a missing key makes i18next return\n * the key itself, which this scan catches regardless of which key\n * regressed.\n */\n\n/**\n * Namespaces of the ui-theme translation file. Restricting the scan to\n * known prefixes keeps false positives out (package names like\n * `lodash.merge` or readme prose with dots must not trip it).\n */\nconst I18N_NAMESPACES = [\n 'security',\n 'dialog',\n 'header',\n 'sidebar',\n 'package',\n 'versions',\n 'search',\n 'form',\n 'form-placeholder',\n 'form-validation',\n 'button',\n 'error',\n 'uplinks',\n 'dependencies',\n 'autoComplete',\n 'footer',\n 'stage',\n 'help',\n].join('|');\n\nconst RAW_KEY_PATTERN = new RegExp(`(?:^|[\\\\s>])(?:${I18N_NAMESPACES})\\\\.[a-zA-Z][a-zA-Z.-]+`);\n\nfunction assertNoRawI18nKeys(context: string) {\n cy.get('body')\n .invoke('text')\n .then((text: string) => {\n const match = text.match(RAW_KEY_PATTERN);\n expect(match, `raw i18n key rendered on ${context}: ${match?.[0] ?? ''}`).to.be.null;\n });\n}\n\nexport function i18nKeyTests(config: RegistryConfig) {\n const { features } = config;\n const { header } = config.testIds;\n\n describe('i18n keys', () => {\n maybeIt(features.i18n.noRawKeysCorePages)('home page must not render raw i18n keys', () => {\n cy.visit(config.registryUrl);\n cy.getByTestId(header.container).should('be.visible');\n assertNoRawI18nKeys('home');\n });\n\n maybeIt(features.i18n.noRawKeysSecurityPages)(\n 'login dialog and security pages must not render raw i18n keys',\n () => {\n // login dialog\n cy.visit(config.registryUrl);\n cy.getByTestId(config.testIds.header.loginButton).click();\n cy.getByTestId(config.testIds.login.dialogContent).should('be.visible');\n assertNoRawI18nKeys('login dialog');\n\n // standalone security pages (render regardless of the `next`\n // param except /-/web/login, which needs a valid one to show\n // the form — the NotFound fallback is translated too, so the\n // scan still applies)\n for (const path of ['/-/web/login', '/-/web/add-user', '/-/web/change-password']) {\n cy.visit(`${config.registryUrl}${path}`, { failOnStatusCode: false });\n cy.get('body').should('be.visible');\n assertNoRawI18nKeys(path);\n }\n }\n );\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,IAAM,kBAAkB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG;AAEV,IAAM,kBAAkB,IAAI,OAAO,kBAAkB,gBAAgB,wBAAwB;AAE7F,SAAS,oBAAoB,SAAiB;CAC5C,GAAG,IAAI,MAAM,CAAC,CACX,OAAO,MAAM,CAAC,CACd,MAAM,SAAiB;EACtB,MAAM,QAAQ,KAAK,MAAM,eAAe;EACxC,OAAO,OAAO,4BAA4B,QAAQ,IAAI,QAAQ,MAAM,IAAI,CAAC,CAAC,GAAG,GAAG;CAClF,CAAC;AACL;AAEA,SAAgB,aAAa,QAAwB;CACnD,MAAM,EAAE,aAAa;CACrB,MAAM,EAAE,WAAW,OAAO;CAE1B,SAAS,mBAAmB;EAC1B,iBAAA,QAAQ,SAAS,KAAK,kBAAkB,CAAC,CAAC,iDAAiD;GACzF,GAAG,MAAM,OAAO,WAAW;GAC3B,GAAG,YAAY,OAAO,SAAS,CAAC,CAAC,OAAO,YAAY;GACpD,oBAAoB,MAAM;EAC5B,CAAC;EAED,iBAAA,QAAQ,SAAS,KAAK,sBAAsB,CAAC,CAC3C,uEACM;GAEJ,GAAG,MAAM,OAAO,WAAW;GAC3B,GAAG,YAAY,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC,MAAM;GACxD,GAAG,YAAY,OAAO,QAAQ,MAAM,aAAa,CAAC,CAAC,OAAO,YAAY;GACtE,oBAAoB,cAAc;GAMlC,KAAK,MAAM,QAAQ;IAAC;IAAgB;IAAmB;GAAwB,GAAG;IAChF,GAAG,MAAM,GAAG,OAAO,cAAc,QAAQ,EAAE,kBAAkB,MAAM,CAAC;IACpE,GAAG,IAAI,MAAM,CAAC,CAAC,OAAO,YAAY;IAClC,oBAAoB,IAAI;GAC1B;EACF,CACF;CACF,CAAC;AACH"}
@@ -0,0 +1,22 @@
1
+ const require_home = require("./home.cjs");
2
+ const require_signin = require("./signin.cjs");
3
+ const require_publish = require("./publish.cjs");
4
+ const require_search = require("./search.cjs");
5
+ const require_settings = require("./settings.cjs");
6
+ const require_layout = require("./layout.cjs");
7
+ const require_change_password = require("./change-password.cjs");
8
+ const require_detail = require("./detail.cjs");
9
+ const require_failure_modes = require("./failure-modes.cjs");
10
+ const require_i18n_keys = require("./i18n-keys.cjs");
11
+ const require_signup = require("./signup.cjs");
12
+ exports.changePasswordTests = require_change_password.changePasswordTests;
13
+ exports.detailTests = require_detail.detailTests;
14
+ exports.failureModeTests = require_failure_modes.failureModeTests;
15
+ exports.homeTests = require_home.homeTests;
16
+ exports.i18nKeyTests = require_i18n_keys.i18nKeyTests;
17
+ exports.layoutTests = require_layout.layoutTests;
18
+ exports.publishTests = require_publish.publishTests;
19
+ exports.searchTests = require_search.searchTests;
20
+ exports.settingsTests = require_settings.settingsTests;
21
+ exports.signinTests = require_signin.signinTests;
22
+ exports.signupTests = require_signup.signupTests;
@@ -0,0 +1,56 @@
1
+ const require_features = require("../features.cjs");
2
+ //#region src/tests/signup.ts
3
+ /**
4
+ * Create-user (web signup) tests.
5
+ *
6
+ * Requires the server flag `createUser: true` (the /-/web/add-user page
7
+ * redirects home without it) and the fixed signup form from
8
+ * verdaccio/verdaccio#6210 — every published ui-theme posted to
9
+ * `PUT /-/web/add-user:<user>`, a URL that only serves the SPA, so the
10
+ * form could never succeed. That is exactly why this suite exists: the
11
+ * bug lived for months because no e2e ever clicked through the flow.
12
+ */
13
+ function signupTests(config) {
14
+ const { features } = config;
15
+ const { signup } = config.selectors;
16
+ describe("signup (create user)", () => {
17
+ beforeEach(() => {
18
+ cy.visit(`${config.registryUrl}/-/web/add-user`);
19
+ cy.get("body").should("be.visible");
20
+ });
21
+ require_features.maybeIt(features.signup.happyPath)("should create a user through the real signup endpoint and land on success", () => {
22
+ const username = `e2e-signup-${Date.now()}`;
23
+ cy.intercept("PUT", "/-/verdaccio/sec/signup").as("signup");
24
+ cy.get(signup.usernameInput).type(username);
25
+ cy.get(signup.passwordInput).type("e2e-password");
26
+ cy.get(signup.emailInput).type(`${username}@example.com`);
27
+ cy.get(signup.submitButton).should("not.be.disabled").click();
28
+ cy.wait("@signup", { timeout: 1e4 }).then((interception) => {
29
+ const body = interception.request.body;
30
+ expect(body.name).to.eq(username);
31
+ expect(body.email).to.eq(`${username}@example.com`);
32
+ expect(body.sessionId).to.be.a("string").with.length(36);
33
+ expect(interception.response?.statusCode).to.be.oneOf([200, 201]);
34
+ });
35
+ cy.location("pathname", { timeout: 1e4 }).should("contain", "/-/web/success");
36
+ });
37
+ require_features.maybeIt(features.signup.validation)("an invalid email must show a visible message and keep submit disabled", () => {
38
+ cy.get(signup.usernameInput).type("e2e-validation-user");
39
+ cy.get(signup.passwordInput).type("e2e-password");
40
+ cy.get(signup.emailInput).type("not-an-email");
41
+ cy.get(signup.submitButton).should("be.disabled");
42
+ cy.get(signup.emailInput).closest("div.MuiFormControl-root").find(".MuiFormHelperText-root").should("be.visible").and("not.be.empty");
43
+ });
44
+ require_features.maybeIt(features.signup.validation)("a username with spaces must block submit with a visible message", () => {
45
+ cy.get(signup.usernameInput).type("user name");
46
+ cy.get(signup.passwordInput).type("e2e-password");
47
+ cy.get(signup.emailInput).type("valid@example.com");
48
+ cy.get(signup.submitButton).should("be.disabled");
49
+ cy.get(signup.usernameInput).closest("div.MuiFormControl-root").find(".MuiFormHelperText-root").should("be.visible").and("not.be.empty");
50
+ });
51
+ });
52
+ }
53
+ //#endregion
54
+ exports.signupTests = signupTests;
55
+
56
+ //# sourceMappingURL=signup.cjs.map