@verdaccio/e2e-ui 2.2.0 → 2.4.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.
- package/build/cjs/commands/index.cjs +7 -0
- package/build/cjs/commands/index.cjs.map +1 -1
- package/build/cjs/features.cjs +73 -0
- package/build/cjs/features.cjs.map +1 -0
- package/build/cjs/index.cjs +8 -1
- package/build/cjs/index.cjs.map +1 -1
- package/build/cjs/testIds.cjs +16 -1
- package/build/cjs/testIds.cjs.map +1 -1
- package/build/cjs/tests/change-password.cjs +133 -0
- package/build/cjs/tests/change-password.cjs.map +1 -0
- package/build/cjs/tests/home.cjs +3 -1
- package/build/cjs/tests/home.cjs.map +1 -1
- package/build/cjs/tests/layout.cjs +10 -0
- package/build/cjs/tests/layout.cjs.map +1 -1
- package/build/cjs/tests/publish.cjs +25 -2
- package/build/cjs/tests/publish.cjs.map +1 -1
- package/build/cjs/tests/search.cjs +48 -0
- package/build/cjs/tests/search.cjs.map +1 -1
- package/build/cjs/tests/settings.cjs +3 -1
- package/build/cjs/tests/settings.cjs.map +1 -1
- package/build/cjs/tests/signin.cjs +25 -3
- package/build/cjs/tests/signin.cjs.map +1 -1
- package/build/commands/index.d.ts +11 -0
- package/build/esm/commands/index.js +7 -0
- package/build/esm/commands/index.js.map +1 -1
- package/build/esm/features.js +71 -0
- package/build/esm/features.js.map +1 -0
- package/build/esm/index.js +6 -2
- package/build/esm/index.js.map +1 -1
- package/build/esm/testIds.js +16 -1
- package/build/esm/testIds.js.map +1 -1
- package/build/esm/tests/change-password.js +133 -0
- package/build/esm/tests/change-password.js.map +1 -0
- package/build/esm/tests/home.js +3 -1
- package/build/esm/tests/home.js.map +1 -1
- package/build/esm/tests/layout.js +10 -0
- package/build/esm/tests/layout.js.map +1 -1
- package/build/esm/tests/publish.js +25 -2
- package/build/esm/tests/publish.js.map +1 -1
- package/build/esm/tests/search.js +48 -0
- package/build/esm/tests/search.js.map +1 -1
- package/build/esm/tests/settings.js +3 -1
- package/build/esm/tests/settings.js.map +1 -1
- package/build/esm/tests/signin.js +25 -3
- package/build/esm/tests/signin.js.map +1 -1
- package/build/features.d.ts +129 -0
- package/build/index.d.ts +3 -1
- package/build/testIds.d.ts +28 -0
- package/build/tests/change-password.d.ts +29 -0
- package/build/tests/index.d.ts +1 -0
- package/build/types.d.ts +8 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","names":[],"sources":["../../../src/tests/settings.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\n\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\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
|
|
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,13 +1,15 @@
|
|
|
1
|
+
import { maybeIt } from "../features.js";
|
|
1
2
|
//#region src/tests/signin.ts
|
|
2
3
|
function signinTests(config) {
|
|
3
|
-
const { header } = config.testIds;
|
|
4
|
+
const { header, login } = config.testIds;
|
|
4
5
|
const { loginDialog } = config.selectors;
|
|
6
|
+
const { features } = config;
|
|
5
7
|
describe("sign in / sign out", () => {
|
|
6
8
|
beforeEach(() => {
|
|
7
9
|
cy.intercept("POST", "/-/verdaccio/sec/login").as("sign");
|
|
10
|
+
cy.visit(config.registryUrl);
|
|
8
11
|
});
|
|
9
12
|
it("should login user", () => {
|
|
10
|
-
cy.visit(config.registryUrl);
|
|
11
13
|
cy.login(config.credentials.user, config.credentials.password, {
|
|
12
14
|
loginButton: header.loginButton,
|
|
13
15
|
...loginDialog
|
|
@@ -18,7 +20,6 @@ function signinTests(config) {
|
|
|
18
20
|
cy.getByTestId(header.greetingsLabel).contains(config.credentials.user);
|
|
19
21
|
});
|
|
20
22
|
it("should logout a user", () => {
|
|
21
|
-
cy.visit(config.registryUrl);
|
|
22
23
|
cy.login(config.credentials.user, config.credentials.password, {
|
|
23
24
|
loginButton: header.loginButton,
|
|
24
25
|
...loginDialog
|
|
@@ -30,6 +31,27 @@ function signinTests(config) {
|
|
|
30
31
|
cy.wait(200);
|
|
31
32
|
cy.getByTestId(header.loginButton).contains("Login");
|
|
32
33
|
});
|
|
34
|
+
maybeIt(features.signin.validationTests)("should disable the submit button while the form is empty", () => {
|
|
35
|
+
cy.getByTestId(header.loginButton).click();
|
|
36
|
+
cy.getByTestId(login.dialog).should("be.visible");
|
|
37
|
+
cy.get(loginDialog.submitButton).should("be.disabled");
|
|
38
|
+
});
|
|
39
|
+
maybeIt(features.signin.validationTests)("should keep the submit button disabled when only the username is filled", () => {
|
|
40
|
+
cy.getByTestId(header.loginButton).click();
|
|
41
|
+
cy.getByTestId(login.dialog).should("be.visible");
|
|
42
|
+
cy.get(loginDialog.usernameInput).type(config.credentials.user);
|
|
43
|
+
cy.get(loginDialog.submitButton).should("be.disabled");
|
|
44
|
+
});
|
|
45
|
+
maybeIt(features.signin.validationTests)("should show an error banner on invalid credentials", () => {
|
|
46
|
+
cy.login(config.credentials.user, "definitely-wrong-password-xyz", {
|
|
47
|
+
loginButton: header.loginButton,
|
|
48
|
+
...loginDialog
|
|
49
|
+
});
|
|
50
|
+
cy.wait("@sign").its("response.statusCode").should("not.eq", 200);
|
|
51
|
+
cy.getByTestId(login.error, { timeout: 5e3 }).should("be.visible");
|
|
52
|
+
cy.getByTestId(login.error).should("contain.text", "Invalid username or password");
|
|
53
|
+
cy.getByTestId(login.dialog).should("be.visible");
|
|
54
|
+
});
|
|
33
55
|
});
|
|
34
56
|
}
|
|
35
57
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signin.js","names":[],"sources":["../../../src/tests/signin.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\n\nimport { RegistryConfig } from '../types';\n\nexport function signinTests(config: RegistryConfig) {\n const { header } = config.testIds;\n const { loginDialog } = config.selectors;\n\n describe('sign in / sign out', () => {\n beforeEach(() => {\n cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');\n });\n\n it('should login user', () => {\n cy.
|
|
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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
/// <reference types="cypress" />
|
|
3
|
+
/**
|
|
4
|
+
* Per-test feature flags for enabling/disabling individual test cases
|
|
5
|
+
* in the e2e-ui suites.
|
|
6
|
+
*
|
|
7
|
+
* The Verdaccio UI behaves differently across branches — search result
|
|
8
|
+
* payload shape differs between the `/-/v1/search` and
|
|
9
|
+
* `/-/verdaccio/data/search/*` endpoints, the language picker was
|
|
10
|
+
* added in a specific minor, etc. Rather than forking the suite per
|
|
11
|
+
* branch, consumers can disable individual tests via
|
|
12
|
+
* `createRegistryConfig({ features: { … } })`.
|
|
13
|
+
*
|
|
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.
|
|
17
|
+
*/
|
|
18
|
+
export interface Features {
|
|
19
|
+
search: {
|
|
20
|
+
/**
|
|
21
|
+
* Whether to run the "results dropdown renders a matching
|
|
22
|
+
* package" test. Disable on builds where the search result shape
|
|
23
|
+
* or the Autocomplete rendering differs from the default assumption.
|
|
24
|
+
*/
|
|
25
|
+
resultsDropdown: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Whether to run the "clicking a result navigates to detail" test.
|
|
28
|
+
* Depends on the Autocomplete `onSelectItem` → router wiring.
|
|
29
|
+
*/
|
|
30
|
+
resultClickNavigation: boolean;
|
|
31
|
+
};
|
|
32
|
+
home: {
|
|
33
|
+
/**
|
|
34
|
+
* Whether to run the `with a published package` sub-block inside
|
|
35
|
+
* `homeTests` (publishes a throwaway package and asserts it
|
|
36
|
+
* renders in the package list).
|
|
37
|
+
*/
|
|
38
|
+
publishedPackageRendering: boolean;
|
|
39
|
+
};
|
|
40
|
+
settings: {
|
|
41
|
+
/**
|
|
42
|
+
* Whether to run the "change language via card click" test.
|
|
43
|
+
* Depends on the `LanguageSwitch` component layout and its
|
|
44
|
+
* translation sentinels ("Translations", "German"). Skip on
|
|
45
|
+
* builds that lag behind the upstream translation file.
|
|
46
|
+
*/
|
|
47
|
+
languageSwitcher: boolean;
|
|
48
|
+
};
|
|
49
|
+
signin: {
|
|
50
|
+
/**
|
|
51
|
+
* Whether to run the three validation tests (disabled submit
|
|
52
|
+
* button, invalid credentials error banner). Depends on the
|
|
53
|
+
* current yup schema and the hard-coded "Invalid username or
|
|
54
|
+
* password" error string.
|
|
55
|
+
*/
|
|
56
|
+
validationTests: boolean;
|
|
57
|
+
};
|
|
58
|
+
layout: {
|
|
59
|
+
/**
|
|
60
|
+
* Whether to run the dark/light theme switch test. Depends on
|
|
61
|
+
* `web.showThemeSwitch` (defaults to true in ui-theme) and the
|
|
62
|
+
* `header--button--light` / `header--button--dark` testids.
|
|
63
|
+
*/
|
|
64
|
+
themeSwitch: boolean;
|
|
65
|
+
};
|
|
66
|
+
publish: {
|
|
67
|
+
/**
|
|
68
|
+
* Whether to run the "tarball download button fires a GET" test.
|
|
69
|
+
* Depends on `web.showDownloadTarball` (defaults to true) and
|
|
70
|
+
* the published package manifest having a valid `dist.tarball`.
|
|
71
|
+
*/
|
|
72
|
+
downloadTarball: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Whether to run the "raw viewer dialog opens + closes" test.
|
|
75
|
+
* Depends on `web.showRaw` (defaults to true).
|
|
76
|
+
*/
|
|
77
|
+
rawViewer: boolean;
|
|
78
|
+
};
|
|
79
|
+
changePassword: {
|
|
80
|
+
/**
|
|
81
|
+
* Whether to run the happy-path test (submit valid change,
|
|
82
|
+
* expect navigation to the success page, then restore the
|
|
83
|
+
* original password in `after()`).
|
|
84
|
+
*
|
|
85
|
+
* The suite targets /-/web/change-password, which renders only
|
|
86
|
+
* when the server is configured with `flags.changePassword: true`.
|
|
87
|
+
* Disable on registries that do not enable the flag.
|
|
88
|
+
*
|
|
89
|
+
* Also disable on **published verdaccio 6.x** (all lines through
|
|
90
|
+
* 6.5.0): the reset_password handler in
|
|
91
|
+
* `verdaccio/build/api/web/api/user.js` ships with an inverted
|
|
92
|
+
* conditional — `validatePassword(...) === false` gates the
|
|
93
|
+
* `auth.changePassword(...)` call, so a *valid* new password
|
|
94
|
+
* always returns HTTP 400 (`PASSWORD_VALIDATION`). The bug is
|
|
95
|
+
* fixed on the development branch but has not been released
|
|
96
|
+
* in any 6.x tag, so the happy path cannot succeed against an
|
|
97
|
+
* `npm install verdaccio@6` runtime.
|
|
98
|
+
*/
|
|
99
|
+
happyPath: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Whether to run the client-side validation tests (submit button
|
|
102
|
+
* stays disabled while fields are empty / mismatched confirm).
|
|
103
|
+
* Depends on the yup `changePasswordSchema`.
|
|
104
|
+
*/
|
|
105
|
+
validation: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Whether to run the "wrong old password shows error banner" test.
|
|
108
|
+
* Depends on the server rejecting the call and the onSubmit catch
|
|
109
|
+
* block surfacing `"Failed to change password"` via
|
|
110
|
+
* `LoginDialogFormError`.
|
|
111
|
+
*/
|
|
112
|
+
wrongOldPassword: boolean;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** Defaults: all flags on. */
|
|
116
|
+
export declare const DEFAULT_FEATURES: Features;
|
|
117
|
+
import type { DeepPartial } from './testIds';
|
|
118
|
+
/**
|
|
119
|
+
* Merge user overrides into the default feature flags. Per-section,
|
|
120
|
+
* one level deep — matching the style of `mergeTestIds`.
|
|
121
|
+
*/
|
|
122
|
+
export declare function mergeFeatures(defaults: Features, overrides?: DeepPartial<Features>): Features;
|
|
123
|
+
/**
|
|
124
|
+
* Helper that returns either `it` or `it.skip` depending on an
|
|
125
|
+
* enabled flag. Usage:
|
|
126
|
+
*
|
|
127
|
+
* maybeIt(features.search.resultsDropdown)('…', () => { … });
|
|
128
|
+
*/
|
|
129
|
+
export declare function maybeIt(enabled: boolean): Mocha.TestFunction | Mocha.PendingTestFunction;
|
package/build/index.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ import { RegistryConfig, VerdaccioUiOptions } from './types';
|
|
|
3
3
|
export type { RegistryConfig, RegistryTaskResult, VerdaccioUiOptions, } from './types';
|
|
4
4
|
export type { PublishPackageInput, PublishPackageTaskInput, PublishPackageResult, UnpublishPackageInput, UnpublishPackageResult, } from './tasks';
|
|
5
5
|
export type { DeepPartial, Selectors, TestIds } from './testIds';
|
|
6
|
+
export type { Features } from './features';
|
|
6
7
|
export { DEFAULT_SELECTORS, DEFAULT_TEST_IDS } from './testIds';
|
|
8
|
+
export { DEFAULT_FEATURES, maybeIt } from './features';
|
|
7
9
|
export { publishPackage, cleanupPublished, unpublishPackage } from './tasks';
|
|
8
|
-
export { homeTests, signinTests, publishTests, searchTests, settingsTests, layoutTests, } from './tests';
|
|
10
|
+
export { homeTests, signinTests, publishTests, searchTests, settingsTests, layoutTests, changePasswordTests, } from './tests';
|
|
9
11
|
/**
|
|
10
12
|
* Build a full RegistryConfig from user-provided options with defaults.
|
|
11
13
|
*
|
package/build/testIds.d.ts
CHANGED
|
@@ -41,6 +41,14 @@ export interface TestIds {
|
|
|
41
41
|
settingsTooltip: string;
|
|
42
42
|
/** Info icon that opens the registry info dialog. */
|
|
43
43
|
infoTooltip: string;
|
|
44
|
+
/**
|
|
45
|
+
* Theme switch button shown while in LIGHT mode (clicking it
|
|
46
|
+
* flips to dark). The underlying component swaps between this
|
|
47
|
+
* and `themeSwitchDark` based on `isDarkMode`.
|
|
48
|
+
*/
|
|
49
|
+
themeSwitchLight: string;
|
|
50
|
+
/** Theme switch button shown while in DARK mode. */
|
|
51
|
+
themeSwitchDark: string;
|
|
44
52
|
/** Menu icon shown after login (opens the logged-in menu). */
|
|
45
53
|
logInDialogIcon: string;
|
|
46
54
|
/** "Log out" entry inside the logged-in menu. */
|
|
@@ -54,6 +62,18 @@ export interface TestIds {
|
|
|
54
62
|
/** "Powered by" version text on the right side of the footer. */
|
|
55
63
|
version: string;
|
|
56
64
|
};
|
|
65
|
+
login: {
|
|
66
|
+
/** Login dialog container (the MUI Dialog root). */
|
|
67
|
+
dialog: string;
|
|
68
|
+
/** DialogContent wrapper inside the login dialog. */
|
|
69
|
+
dialogContent: string;
|
|
70
|
+
/**
|
|
71
|
+
* Error banner shown inside the login dialog when the server
|
|
72
|
+
* rejects credentials (or any other `errors.root` message the form
|
|
73
|
+
* sets). Renders inside the `LoginDialogFormError` component.
|
|
74
|
+
*/
|
|
75
|
+
error: string;
|
|
76
|
+
};
|
|
57
77
|
package: {
|
|
58
78
|
/** Wrapper around the list of packages on the home page. */
|
|
59
79
|
itemList: string;
|
|
@@ -85,6 +105,14 @@ export interface TestIds {
|
|
|
85
105
|
uplinksTab: string;
|
|
86
106
|
/** Empty-state message when the package has no uplinks. */
|
|
87
107
|
noUplinks: string;
|
|
108
|
+
/** Action-bar tarball download FAB. */
|
|
109
|
+
downloadTarballBtn: string;
|
|
110
|
+
/** Action-bar "view raw manifest" FAB. */
|
|
111
|
+
rawBtn: string;
|
|
112
|
+
/** Full-screen dialog that opens when `rawBtn` is clicked. */
|
|
113
|
+
rawViewerDialog: string;
|
|
114
|
+
/** Close button inside the raw viewer dialog. */
|
|
115
|
+
closeRawViewer: string;
|
|
88
116
|
};
|
|
89
117
|
}
|
|
90
118
|
/**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { RegistryConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Tests for the Change Password page at /-/web/change-password.
|
|
4
|
+
*
|
|
5
|
+
* The page renders only when the server is started with
|
|
6
|
+
* `flags.changePassword: true` (otherwise the React component
|
|
7
|
+
* redirects to `/` on mount). Each test logs in first, navigates
|
|
8
|
+
* directly to the page, and drives the form.
|
|
9
|
+
*
|
|
10
|
+
* Selector strategy: the ChangePassword form does not ship stable
|
|
11
|
+
* `id`/testid attributes on its inputs, but every field is registered
|
|
12
|
+
* via react-hook-form's `register('<name>')`, which sets a stable
|
|
13
|
+
* `name` attribute on the underlying `<input>`. The labels themselves
|
|
14
|
+
* are `t('security.changePassword.*')` calls — when the i18n bundle
|
|
15
|
+
* hasn't finished loading (or isn't loaded at all on this route), MUI
|
|
16
|
+
* renders the literal i18n key as the label, so any selector that
|
|
17
|
+
* matches on visible label text silently misses every field and the
|
|
18
|
+
* form stays empty (which would also make the mismatch test "pass"
|
|
19
|
+
* for the wrong reason: the submit button is disabled because the
|
|
20
|
+
* form is empty, not because yup rejected the mismatch).
|
|
21
|
+
*
|
|
22
|
+
* The stable, i18n-independent contract from ChangePassword.tsx is:
|
|
23
|
+
* register('username') → input[name="username"]
|
|
24
|
+
* register('oldPassword') → input[name="oldPassword"]
|
|
25
|
+
* register('newPassword') → input[name="newPassword"]
|
|
26
|
+
* register('confirmPassword') → input[name="confirmPassword"]
|
|
27
|
+
* submit button → form button[type="submit"]
|
|
28
|
+
*/
|
|
29
|
+
export declare function changePasswordTests(config: RegistryConfig): void;
|
package/build/tests/index.d.ts
CHANGED
package/build/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="cypress" />
|
|
2
2
|
/// <reference types="cypress" />
|
|
3
3
|
/// <reference types="cypress" />
|
|
4
|
+
import type { Features } from './features';
|
|
4
5
|
import type { PublishPackageResult, PublishPackageTaskInput, UnpublishPackageInput, UnpublishPackageResult } from './tasks';
|
|
5
6
|
import type { DeepPartial, Selectors, TestIds } from './testIds';
|
|
6
7
|
export type RegistryConfig = {
|
|
@@ -15,6 +16,11 @@ export type RegistryConfig = {
|
|
|
15
16
|
testIds: TestIds;
|
|
16
17
|
/** Fully-resolved CSS selector map (defaults merged with overrides). */
|
|
17
18
|
selectors: Selectors;
|
|
19
|
+
/**
|
|
20
|
+
* Per-test feature flags — set to `false` to convert a specific
|
|
21
|
+
* test into `it.skip` without forking the suite.
|
|
22
|
+
*/
|
|
23
|
+
features: Features;
|
|
18
24
|
};
|
|
19
25
|
export type VerdaccioUiOptions = {
|
|
20
26
|
registryUrl: string;
|
|
@@ -28,6 +34,8 @@ export type VerdaccioUiOptions = {
|
|
|
28
34
|
testIds?: DeepPartial<TestIds>;
|
|
29
35
|
/** Partial override of the default CSS selector map. */
|
|
30
36
|
selectors?: DeepPartial<Selectors>;
|
|
37
|
+
/** Partial override of the default feature flags. */
|
|
38
|
+
features?: DeepPartial<Features>;
|
|
31
39
|
};
|
|
32
40
|
/**
|
|
33
41
|
* Return value of the `registry` Cypress task — exposed as a named
|