@verdaccio/e2e-ui 2.1.0 → 2.2.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/README.md +134 -0
- package/build/cjs/commands/index.cjs +32 -0
- package/build/cjs/commands/index.cjs.map +1 -0
- package/build/cjs/index.cjs +128 -0
- package/build/cjs/index.cjs.map +1 -0
- package/build/cjs/tasks/publish.cjs +245 -0
- package/build/cjs/tasks/publish.cjs.map +1 -0
- package/build/cjs/testIds.cjs +104 -0
- package/build/cjs/testIds.cjs.map +1 -0
- package/build/cjs/tests/home.cjs +88 -0
- package/build/cjs/tests/home.cjs.map +1 -0
- package/build/cjs/tests/layout.cjs +71 -0
- package/build/cjs/tests/layout.cjs.map +1 -0
- package/build/cjs/tests/publish.cjs +129 -0
- package/build/cjs/tests/publish.cjs.map +1 -0
- package/build/cjs/tests/search.cjs +74 -0
- package/build/cjs/tests/search.cjs.map +1 -0
- package/build/cjs/tests/settings.cjs +66 -0
- package/build/cjs/tests/settings.cjs.map +1 -0
- package/build/cjs/tests/signin.cjs +38 -0
- package/build/cjs/tests/signin.cjs.map +1 -0
- package/build/commands/index.d.ts +29 -0
- package/build/esm/commands/index.js +33 -0
- package/build/esm/commands/index.js.map +1 -0
- package/build/esm/index.js +114 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/tasks/publish.js +243 -0
- package/build/esm/tasks/publish.js.map +1 -0
- package/build/esm/testIds.js +101 -0
- package/build/esm/testIds.js.map +1 -0
- package/build/esm/tests/home.js +88 -0
- package/build/esm/tests/home.js.map +1 -0
- package/build/esm/tests/layout.js +71 -0
- package/build/esm/tests/layout.js.map +1 -0
- package/build/esm/tests/publish.js +129 -0
- package/build/esm/tests/publish.js.map +1 -0
- package/build/esm/tests/search.js +74 -0
- package/build/esm/tests/search.js.map +1 -0
- package/build/esm/tests/settings.js +66 -0
- package/build/esm/tests/settings.js.map +1 -0
- package/build/esm/tests/signin.js +38 -0
- package/build/esm/tests/signin.js.map +1 -0
- package/build/index.d.ts +55 -0
- package/build/tasks/index.d.ts +2 -0
- package/build/tasks/publish.d.ts +94 -0
- package/build/testIds.d.ts +130 -0
- package/build/tests/home.d.ts +2 -0
- package/build/tests/index.d.ts +6 -0
- package/build/tests/layout.d.ts +12 -0
- package/build/tests/publish.d.ts +2 -0
- package/build/tests/search.d.ts +10 -0
- package/build/tests/settings.d.ts +19 -0
- package/build/tests/signin.d.ts +2 -0
- package/build/types.d.ts +102 -0
- package/package.json +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//#region src/tests/home.ts
|
|
2
|
+
function homeTests(config) {
|
|
3
|
+
const { home, header, package: pkg } = config.testIds;
|
|
4
|
+
describe("home", () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
cy.intercept("GET", "/-/verdaccio/data/packages").as("pkgs");
|
|
7
|
+
cy.visit(config.registryUrl);
|
|
8
|
+
cy.get("body").should("be.visible");
|
|
9
|
+
});
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cy.wait(2e3);
|
|
12
|
+
});
|
|
13
|
+
it("title should be correct", () => {
|
|
14
|
+
cy.location("pathname").should("include", "/");
|
|
15
|
+
cy.title().should("eq", config.title);
|
|
16
|
+
});
|
|
17
|
+
it("should fetch the package list from the API", () => {
|
|
18
|
+
cy.wait("@pkgs", { timeout: 1e4 }).then((interception) => {
|
|
19
|
+
const status = interception.response?.statusCode;
|
|
20
|
+
expect(status).to.be.oneOf([200, 304]);
|
|
21
|
+
if (status === 200) expect(interception.response?.body).to.be.an("array");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it("should match title with no packages published", () => {
|
|
25
|
+
cy.wait("@pkgs");
|
|
26
|
+
cy.getByTestId(home.helpCard, { timeout: 1e4 }).should("be.visible");
|
|
27
|
+
cy.getByTestId(home.helpCard).contains("No Package Published Yet.");
|
|
28
|
+
});
|
|
29
|
+
it("should display instructions on help card", () => {
|
|
30
|
+
cy.wait("@pkgs");
|
|
31
|
+
cy.getByTestId(home.helpCard, { timeout: 1e4 }).should("be.visible");
|
|
32
|
+
cy.getByTestId(home.helpCard).contains(`npm adduser --registry ${config.registryUrl}`);
|
|
33
|
+
cy.getByTestId(home.helpCard).contains(`npm publish --registry ${config.registryUrl}`);
|
|
34
|
+
});
|
|
35
|
+
it("should render the header logo and login button", () => {
|
|
36
|
+
cy.getByTestId(header.container).should("be.visible");
|
|
37
|
+
cy.get(`[data-testid="${header.defaultLogo}"], [data-testid="${header.customLogo}"]`).should("be.visible");
|
|
38
|
+
cy.getByTestId(header.loginButton).should("be.visible");
|
|
39
|
+
});
|
|
40
|
+
it("should navigate back to home when clicking the header logo", () => {
|
|
41
|
+
cy.visit(`${config.registryUrl}/-/web/detail/@verdaccio/not-found`);
|
|
42
|
+
cy.getByTestId(home.notFound, { timeout: 1e4 }).should("be.visible");
|
|
43
|
+
cy.get(`[data-testid="${header.defaultLogo}"], [data-testid="${header.customLogo}"]`).first().click();
|
|
44
|
+
cy.location("pathname").should("eq", "/");
|
|
45
|
+
cy.getByTestId(home.helpCard).should("be.visible");
|
|
46
|
+
});
|
|
47
|
+
it("should go to 404 page", () => {
|
|
48
|
+
cy.visit(`${config.registryUrl}/-/web/detail/@verdaccio/not-found`);
|
|
49
|
+
cy.getByTestId(home.notFound, { timeout: 1e4 }).should("be.visible");
|
|
50
|
+
cy.getByTestId(home.notFound).contains("Sorry, we couldn't find it.");
|
|
51
|
+
});
|
|
52
|
+
describe("with a published package", () => {
|
|
53
|
+
const pkgName = "@verdaccio/home-fixture";
|
|
54
|
+
let tempFolder = null;
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
cy.task("publishPackage", {
|
|
57
|
+
pkgName,
|
|
58
|
+
version: "1.0.0",
|
|
59
|
+
unique: true
|
|
60
|
+
}).then((result) => {
|
|
61
|
+
tempFolder = result?.tempFolder ?? null;
|
|
62
|
+
});
|
|
63
|
+
cy.visit(config.registryUrl);
|
|
64
|
+
});
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
cy.task("unpublishPackage", {
|
|
67
|
+
pkgName,
|
|
68
|
+
tempFolder: tempFolder ?? void 0
|
|
69
|
+
});
|
|
70
|
+
if (tempFolder) cy.task("cleanupPublished", tempFolder);
|
|
71
|
+
tempFolder = null;
|
|
72
|
+
});
|
|
73
|
+
it("should render the package list when a package exists", () => {
|
|
74
|
+
cy.wait("@pkgs");
|
|
75
|
+
cy.getByTestId(pkg.itemList).should("be.visible");
|
|
76
|
+
cy.getByTestId(pkg.title).should("have.length.at.least", 1);
|
|
77
|
+
});
|
|
78
|
+
it("should show the published package name in the list", () => {
|
|
79
|
+
cy.wait("@pkgs");
|
|
80
|
+
cy.contains(`[data-testid="${pkg.title}"]`, pkgName).should("be.visible");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { homeTests };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=home.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"home.js","names":[],"sources":["../../../src/tests/home.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\n\nimport { RegistryConfig } from '../types';\n\nexport function homeTests(config: RegistryConfig) {\n const { home, header, package: pkg } = config.testIds;\n\n describe('home', () => {\n beforeEach(() => {\n cy.intercept('GET', '/-/verdaccio/data/packages').as('pkgs');\n cy.visit(config.registryUrl);\n // Wait for the app to render\n cy.get('body').should('be.visible');\n });\n\n afterEach(() => {\n cy.wait(2000);\n });\n\n it('title should be correct', () => {\n cy.location('pathname').should('include', '/');\n cy.title().should('eq', config.title);\n });\n\n it('should fetch the package list from the API', () => {\n // The home page always fires /-/verdaccio/data/packages on mount;\n // verifying the endpoint is healthy is the cheapest way to catch\n // registry-side regressions before any DOM assertions.\n //\n // Accept both 200 (fresh fetch) and 304 (cache hit from a prior\n // spec run in the same session) — both indicate a healthy endpoint.\n // 304 responses have no body, so only assert array-shape on 200.\n cy.wait('@pkgs', { timeout: 10000 }).then((interception) => {\n const status = interception.response?.statusCode;\n expect(status).to.be.oneOf([200, 304]);\n if (status === 200) {\n expect(interception.response?.body).to.be.an('array');\n }\n });\n });\n\n it('should match title with no packages published', () => {\n cy.wait('@pkgs');\n cy.getByTestId(home.helpCard, { timeout: 10000 }).should('be.visible');\n cy.getByTestId(home.helpCard).contains('No Package Published Yet.');\n });\n\n it('should display instructions on help card', () => {\n cy.wait('@pkgs');\n cy.getByTestId(home.helpCard, { timeout: 10000 }).should('be.visible');\n cy.getByTestId(home.helpCard).contains(\n `npm adduser --registry ${config.registryUrl}`\n );\n cy.getByTestId(home.helpCard).contains(\n `npm publish --registry ${config.registryUrl}`\n );\n });\n\n it('should render the header logo and login button', () => {\n cy.getByTestId(header.container).should('be.visible');\n cy.get(\n `[data-testid=\"${header.defaultLogo}\"], [data-testid=\"${header.customLogo}\"]`\n ).should('be.visible');\n cy.getByTestId(header.loginButton).should('be.visible');\n });\n\n it('should navigate back to home when clicking the header logo', () => {\n // Land on the 404 page, then click the logo — URL should reset.\n cy.visit(`${config.registryUrl}/-/web/detail/@verdaccio/not-found`);\n cy.getByTestId(home.notFound, { timeout: 10000 }).should('be.visible');\n cy.get(\n `[data-testid=\"${header.defaultLogo}\"], [data-testid=\"${header.customLogo}\"]`\n )\n .first()\n .click();\n cy.location('pathname').should('eq', '/');\n cy.getByTestId(home.helpCard).should('be.visible');\n });\n\n it('should go to 404 page', () => {\n cy.visit(`${config.registryUrl}/-/web/detail/@verdaccio/not-found`);\n cy.getByTestId(home.notFound, { timeout: 10000 }).should('be.visible');\n cy.getByTestId(home.notFound).contains(\"Sorry, we couldn't find it.\");\n });\n\n // ── Rendering assertions that require real package data ─────────\n // Publishes a throwaway package before each test in this block so\n // we can verify the home page actually renders the list (not just\n // the empty state) and cleans up after each test so the outer\n // \"empty registry\" assertions above keep working in isolation.\n describe('with a published package', () => {\n const pkgName = '@verdaccio/home-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 // Re-visit so the page picks up the just-published package.\n cy.visit(config.registryUrl);\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 it('should render the package list when a package exists', () => {\n cy.wait('@pkgs');\n cy.getByTestId(pkg.itemList).should('be.visible');\n cy.getByTestId(pkg.title).should('have.length.at.least', 1);\n });\n\n it('should show the published package name in the list', () => {\n cy.wait('@pkgs');\n cy.contains(`[data-testid=\"${pkg.title}\"]`, pkgName).should(\n 'be.visible'\n );\n });\n });\n });\n}\n"],"mappings":";AAIA,SAAgB,UAAU,QAAwB;CAChD,MAAM,EAAE,MAAM,QAAQ,SAAS,QAAQ,OAAO;AAE9C,UAAS,cAAc;AACrB,mBAAiB;AACf,MAAG,UAAU,OAAO,6BAA6B,CAAC,GAAG,OAAO;AAC5D,MAAG,MAAM,OAAO,YAAY;AAE5B,MAAG,IAAI,OAAO,CAAC,OAAO,aAAa;IACnC;AAEF,kBAAgB;AACd,MAAG,KAAK,IAAK;IACb;AAEF,KAAG,iCAAiC;AAClC,MAAG,SAAS,WAAW,CAAC,OAAO,WAAW,IAAI;AAC9C,MAAG,OAAO,CAAC,OAAO,MAAM,OAAO,MAAM;IACrC;AAEF,KAAG,oDAAoD;AAQrD,MAAG,KAAK,SAAS,EAAE,SAAS,KAAO,CAAC,CAAC,MAAM,iBAAiB;IAC1D,MAAM,SAAS,aAAa,UAAU;AACtC,WAAO,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;AACtC,QAAI,WAAW,IACb,QAAO,aAAa,UAAU,KAAK,CAAC,GAAG,GAAG,GAAG,QAAQ;KAEvD;IACF;AAEF,KAAG,uDAAuD;AACxD,MAAG,KAAK,QAAQ;AAChB,MAAG,YAAY,KAAK,UAAU,EAAE,SAAS,KAAO,CAAC,CAAC,OAAO,aAAa;AACtE,MAAG,YAAY,KAAK,SAAS,CAAC,SAAS,4BAA4B;IACnE;AAEF,KAAG,kDAAkD;AACnD,MAAG,KAAK,QAAQ;AAChB,MAAG,YAAY,KAAK,UAAU,EAAE,SAAS,KAAO,CAAC,CAAC,OAAO,aAAa;AACtE,MAAG,YAAY,KAAK,SAAS,CAAC,SAC5B,0BAA0B,OAAO,cAClC;AACD,MAAG,YAAY,KAAK,SAAS,CAAC,SAC5B,0BAA0B,OAAO,cAClC;IACD;AAEF,KAAG,wDAAwD;AACzD,MAAG,YAAY,OAAO,UAAU,CAAC,OAAO,aAAa;AACrD,MAAG,IACD,iBAAiB,OAAO,YAAY,oBAAoB,OAAO,WAAW,IAC3E,CAAC,OAAO,aAAa;AACtB,MAAG,YAAY,OAAO,YAAY,CAAC,OAAO,aAAa;IACvD;AAEF,KAAG,oEAAoE;AAErE,MAAG,MAAM,GAAG,OAAO,YAAY,oCAAoC;AACnE,MAAG,YAAY,KAAK,UAAU,EAAE,SAAS,KAAO,CAAC,CAAC,OAAO,aAAa;AACtE,MAAG,IACD,iBAAiB,OAAO,YAAY,oBAAoB,OAAO,WAAW,IAC3E,CACE,OAAO,CACP,OAAO;AACV,MAAG,SAAS,WAAW,CAAC,OAAO,MAAM,IAAI;AACzC,MAAG,YAAY,KAAK,SAAS,CAAC,OAAO,aAAa;IAClD;AAEF,KAAG,+BAA+B;AAChC,MAAG,MAAM,GAAG,OAAO,YAAY,oCAAoC;AACnE,MAAG,YAAY,KAAK,UAAU,EAAE,SAAS,KAAO,CAAC,CAAC,OAAO,aAAa;AACtE,MAAG,YAAY,KAAK,SAAS,CAAC,SAAS,8BAA8B;IACrE;AAOF,WAAS,kCAAkC;GACzC,MAAM,UAAU;GAChB,IAAI,aAA4B;AAEhC,oBAAiB;AACf,OAAG,KAAK,kBAAkB;KACxB;KACA,SAAS;KACT,QAAQ;KACT,CAAC,CAAC,MAAM,WAAW;AAClB,kBAAa,QAAQ,cAAc;MACnC;AAEF,OAAG,MAAM,OAAO,YAAY;KAC5B;AAEF,mBAAgB;AACd,OAAG,KAAK,oBAAoB;KAC1B;KACA,YAAY,cAAc,KAAA;KAC3B,CAAC;AACF,QAAI,WACF,IAAG,KAAK,oBAAoB,WAAW;AAEzC,iBAAa;KACb;AAEF,MAAG,8DAA8D;AAC/D,OAAG,KAAK,QAAQ;AAChB,OAAG,YAAY,IAAI,SAAS,CAAC,OAAO,aAAa;AACjD,OAAG,YAAY,IAAI,MAAM,CAAC,OAAO,wBAAwB,EAAE;KAC3D;AAEF,MAAG,4DAA4D;AAC7D,OAAG,KAAK,QAAQ;AAChB,OAAG,SAAS,iBAAiB,IAAI,MAAM,KAAK,QAAQ,CAAC,OACnD,aACD;KACD;IACF;GACF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//#region src/tests/layout.ts
|
|
2
|
+
/**
|
|
3
|
+
* Tests for the persistent page chrome: the header (nav bar, logo,
|
|
4
|
+
* search container, action buttons) and the footer (version marker).
|
|
5
|
+
* Also asserts that the runtime UI configuration endpoint
|
|
6
|
+
* `/-/static/ui-options.js` loads successfully — this endpoint emits
|
|
7
|
+
* `window.__VERDACCIO_BASENAME_UI_OPTIONS`, which the ui-theme relies
|
|
8
|
+
* on for feature flags like showFooter / showSearch / showSettings. If
|
|
9
|
+
* it fails the whole SPA degrades to whatever defaults the provider
|
|
10
|
+
* ships with, so it's worth a direct network-level check.
|
|
11
|
+
*/
|
|
12
|
+
function layoutTests(config) {
|
|
13
|
+
const { header, footer } = config.testIds;
|
|
14
|
+
describe("layout: header, footer, ui-options", () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
cy.intercept("GET", "**/-/static/ui-options.js").as("uiOptions");
|
|
17
|
+
cy.visit(config.registryUrl);
|
|
18
|
+
cy.get("body").should("be.visible");
|
|
19
|
+
});
|
|
20
|
+
it("should load /-/static/ui-options.js with HTTP 200", () => {
|
|
21
|
+
cy.wait("@uiOptions", { timeout: 1e4 }).its("response.statusCode").should("eq", 200);
|
|
22
|
+
});
|
|
23
|
+
it("should serve ui-options.js with a JavaScript content-type", () => {
|
|
24
|
+
cy.wait("@uiOptions", { timeout: 1e4 }).then((interception) => {
|
|
25
|
+
const contentType = interception.response?.headers?.["content-type"] || "";
|
|
26
|
+
expect(contentType).to.match(/javascript/i);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it("should expose window.__VERDACCIO_BASENAME_UI_OPTIONS at runtime", () => {
|
|
30
|
+
cy.wait("@uiOptions", { timeout: 1e4 });
|
|
31
|
+
cy.window().its("__VERDACCIO_BASENAME_UI_OPTIONS").should("be.an", "object");
|
|
32
|
+
});
|
|
33
|
+
describe("header", () => {
|
|
34
|
+
it("should render the header container", () => {
|
|
35
|
+
cy.getByTestId(header.container).should("be.visible");
|
|
36
|
+
cy.getByTestId(header.innerNavBar).should("be.visible");
|
|
37
|
+
});
|
|
38
|
+
it("should render the logo", () => {
|
|
39
|
+
cy.get(`[data-testid="${header.defaultLogo}"], [data-testid="${header.customLogo}"]`).should("be.visible");
|
|
40
|
+
});
|
|
41
|
+
it("should render the search container", () => {
|
|
42
|
+
cy.getByTestId(header.searchContainer).should("be.visible");
|
|
43
|
+
});
|
|
44
|
+
it("should render the header-right action cluster", () => {
|
|
45
|
+
cy.getByTestId(header.right).should("be.visible");
|
|
46
|
+
});
|
|
47
|
+
it("should render the login button when logged out", () => {
|
|
48
|
+
cy.getByTestId(header.loginButton).should("be.visible");
|
|
49
|
+
});
|
|
50
|
+
it("should render the settings and info buttons", () => {
|
|
51
|
+
cy.getByTestId(header.settingsTooltip).should("be.visible");
|
|
52
|
+
cy.getByTestId(header.infoTooltip).should("be.visible");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("footer", () => {
|
|
56
|
+
it("should render the footer container", () => {
|
|
57
|
+
cy.getByTestId(footer.container).scrollIntoView().should("be.visible");
|
|
58
|
+
});
|
|
59
|
+
it("should render the version marker with the powered-by label", () => {
|
|
60
|
+
cy.getByTestId(footer.version).scrollIntoView().should("be.visible").invoke("text").should("have.length.greaterThan", 0);
|
|
61
|
+
});
|
|
62
|
+
it("should render a logo next to the version marker", () => {
|
|
63
|
+
cy.getByTestId(footer.container).find(`[data-testid="${header.defaultLogo}"], [data-testid="${header.customLogo}"]`).should("exist");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { layoutTests };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","names":[],"sources":["../../../src/tests/layout.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\n\nimport { RegistryConfig } from '../types';\n\n/**\n * Tests for the persistent page chrome: the header (nav bar, logo,\n * search container, action buttons) and the footer (version marker).\n * Also asserts that the runtime UI configuration endpoint\n * `/-/static/ui-options.js` loads successfully — this endpoint emits\n * `window.__VERDACCIO_BASENAME_UI_OPTIONS`, which the ui-theme relies\n * on for feature flags like showFooter / showSearch / showSettings. If\n * it fails the whole SPA degrades to whatever defaults the provider\n * ships with, so it's worth a direct network-level check.\n */\nexport function layoutTests(config: RegistryConfig) {\n const { header, footer } = config.testIds;\n\n describe('layout: header, footer, ui-options', () => {\n beforeEach(() => {\n cy.intercept('GET', '**/-/static/ui-options.js').as('uiOptions');\n cy.visit(config.registryUrl);\n cy.get('body').should('be.visible');\n });\n\n it('should load /-/static/ui-options.js with HTTP 200', () => {\n cy.wait('@uiOptions', { timeout: 10000 })\n .its('response.statusCode')\n .should('eq', 200);\n });\n\n it('should serve ui-options.js with a JavaScript content-type', () => {\n cy.wait('@uiOptions', { timeout: 10000 }).then((interception: any) => {\n const contentType =\n interception.response?.headers?.['content-type'] || '';\n // Verdaccio serves this as `application/javascript` (possibly\n // with a charset suffix). Match loosely so small header tweaks\n // don't break the test.\n expect(contentType).to.match(/javascript/i);\n });\n });\n\n it('should expose window.__VERDACCIO_BASENAME_UI_OPTIONS at runtime', () => {\n cy.wait('@uiOptions', { timeout: 10000 });\n // ui-options.js sets this global before the React app boots, so\n // by the time the body is visible it should already be populated.\n cy.window()\n .its('__VERDACCIO_BASENAME_UI_OPTIONS')\n .should('be.an', 'object');\n });\n\n describe('header', () => {\n it('should render the header container', () => {\n cy.getByTestId(header.container).should('be.visible');\n cy.getByTestId(header.innerNavBar).should('be.visible');\n });\n\n it('should render the logo', () => {\n // Either the default SVG logo or a user-provided custom one.\n cy.get(\n `[data-testid=\"${header.defaultLogo}\"], [data-testid=\"${header.customLogo}\"]`\n ).should('be.visible');\n });\n\n it('should render the search container', () => {\n cy.getByTestId(header.searchContainer).should('be.visible');\n });\n\n it('should render the header-right action cluster', () => {\n cy.getByTestId(header.right).should('be.visible');\n });\n\n it('should render the login button when logged out', () => {\n cy.getByTestId(header.loginButton).should('be.visible');\n });\n\n it('should render the settings and info buttons', () => {\n // Both depend on `showSettings` / `showInfo` being truthy in\n // the ui-options response. On the default config provider\n // they default to true so no explicit registry config is\n // required.\n cy.getByTestId(header.settingsTooltip).should('be.visible');\n cy.getByTestId(header.infoTooltip).should('be.visible');\n });\n });\n\n describe('footer', () => {\n it('should render the footer container', () => {\n cy.getByTestId(footer.container).scrollIntoView().should('be.visible');\n });\n\n it('should render the version marker with the powered-by label', () => {\n // <PoweredBy> only renders when `configOptions.version` is\n // truthy, which Verdaccio sets automatically from its\n // package.json at startup.\n cy.getByTestId(footer.version)\n .scrollIntoView()\n .should('be.visible')\n .invoke('text')\n .should('have.length.greaterThan', 0);\n });\n\n it('should render a logo next to the version marker', () => {\n // The footer uses the default SVG logo as the link to\n // verdaccio.org.\n cy.getByTestId(footer.container)\n .find(\n `[data-testid=\"${header.defaultLogo}\"], [data-testid=\"${header.customLogo}\"]`\n )\n .should('exist');\n });\n });\n });\n}\n"],"mappings":";;;;;;;;;;;AAcA,SAAgB,YAAY,QAAwB;CAClD,MAAM,EAAE,QAAQ,WAAW,OAAO;AAElC,UAAS,4CAA4C;AACnD,mBAAiB;AACf,MAAG,UAAU,OAAO,4BAA4B,CAAC,GAAG,YAAY;AAChE,MAAG,MAAM,OAAO,YAAY;AAC5B,MAAG,IAAI,OAAO,CAAC,OAAO,aAAa;IACnC;AAEF,KAAG,2DAA2D;AAC5D,MAAG,KAAK,cAAc,EAAE,SAAS,KAAO,CAAC,CACtC,IAAI,sBAAsB,CAC1B,OAAO,MAAM,IAAI;IACpB;AAEF,KAAG,mEAAmE;AACpE,MAAG,KAAK,cAAc,EAAE,SAAS,KAAO,CAAC,CAAC,MAAM,iBAAsB;IACpE,MAAM,cACJ,aAAa,UAAU,UAAU,mBAAmB;AAItD,WAAO,YAAY,CAAC,GAAG,MAAM,cAAc;KAC3C;IACF;AAEF,KAAG,yEAAyE;AAC1E,MAAG,KAAK,cAAc,EAAE,SAAS,KAAO,CAAC;AAGzC,MAAG,QAAQ,CACR,IAAI,kCAAkC,CACtC,OAAO,SAAS,SAAS;IAC5B;AAEF,WAAS,gBAAgB;AACvB,MAAG,4CAA4C;AAC7C,OAAG,YAAY,OAAO,UAAU,CAAC,OAAO,aAAa;AACrD,OAAG,YAAY,OAAO,YAAY,CAAC,OAAO,aAAa;KACvD;AAEF,MAAG,gCAAgC;AAEjC,OAAG,IACD,iBAAiB,OAAO,YAAY,oBAAoB,OAAO,WAAW,IAC3E,CAAC,OAAO,aAAa;KACtB;AAEF,MAAG,4CAA4C;AAC7C,OAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO,aAAa;KAC3D;AAEF,MAAG,uDAAuD;AACxD,OAAG,YAAY,OAAO,MAAM,CAAC,OAAO,aAAa;KACjD;AAEF,MAAG,wDAAwD;AACzD,OAAG,YAAY,OAAO,YAAY,CAAC,OAAO,aAAa;KACvD;AAEF,MAAG,qDAAqD;AAKtD,OAAG,YAAY,OAAO,gBAAgB,CAAC,OAAO,aAAa;AAC3D,OAAG,YAAY,OAAO,YAAY,CAAC,OAAO,aAAa;KACvD;IACF;AAEF,WAAS,gBAAgB;AACvB,MAAG,4CAA4C;AAC7C,OAAG,YAAY,OAAO,UAAU,CAAC,gBAAgB,CAAC,OAAO,aAAa;KACtE;AAEF,MAAG,oEAAoE;AAIrE,OAAG,YAAY,OAAO,QAAQ,CAC3B,gBAAgB,CAChB,OAAO,aAAa,CACpB,OAAO,OAAO,CACd,OAAO,2BAA2B,EAAE;KACvC;AAEF,MAAG,yDAAyD;AAG1D,OAAG,YAAY,OAAO,UAAU,CAC7B,KACC,iBAAiB,OAAO,YAAY,oBAAoB,OAAO,WAAW,IAC3E,CACA,OAAO,QAAQ;KAClB;IACF;GACF"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
//#region src/tests/publish.ts
|
|
2
|
+
function publishTests(config) {
|
|
3
|
+
const { header, package: pkg } = config.testIds;
|
|
4
|
+
const { markdownBody, loginDialog } = config.selectors;
|
|
5
|
+
describe("publish", () => {
|
|
6
|
+
const pkgName = "@verdaccio/pkg-scoped";
|
|
7
|
+
let tempFolder = null;
|
|
8
|
+
/**
|
|
9
|
+
* Log in once, reuse the session across every test in this suite.
|
|
10
|
+
* `cy.session` caches cookies + localStorage keyed on the first
|
|
11
|
+
* argument, so subsequent calls restore without hitting the network.
|
|
12
|
+
*/
|
|
13
|
+
const loginOnce = () => {
|
|
14
|
+
cy.session(["publish-suite", config.credentials.user], () => {
|
|
15
|
+
cy.visit(config.registryUrl);
|
|
16
|
+
cy.login(config.credentials.user, config.credentials.password, {
|
|
17
|
+
loginButton: header.loginButton,
|
|
18
|
+
...loginDialog
|
|
19
|
+
});
|
|
20
|
+
cy.wait("@sign");
|
|
21
|
+
}, {
|
|
22
|
+
validate() {
|
|
23
|
+
cy.request({
|
|
24
|
+
url: `${config.registryUrl}/-/verdaccio/data/packages`,
|
|
25
|
+
failOnStatusCode: false
|
|
26
|
+
}).its("status").should("be.oneOf", [200, 304]);
|
|
27
|
+
},
|
|
28
|
+
cacheAcrossSpecs: true
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
cy.intercept("POST", "/-/verdaccio/sec/login").as("sign");
|
|
33
|
+
cy.intercept("GET", "/-/verdaccio/data/packages").as("pkgs");
|
|
34
|
+
cy.intercept("GET", `/-/verdaccio/data/sidebar/${pkgName}`).as("sidebar");
|
|
35
|
+
cy.intercept("GET", `/-/verdaccio/data/package/readme/${pkgName}`).as("readme");
|
|
36
|
+
cy.task("publishPackage", {
|
|
37
|
+
pkgName,
|
|
38
|
+
version: "1.0.0",
|
|
39
|
+
dependencies: { debug: "4.0.0" },
|
|
40
|
+
unique: true
|
|
41
|
+
}).then((result) => {
|
|
42
|
+
tempFolder = result?.tempFolder ?? null;
|
|
43
|
+
});
|
|
44
|
+
loginOnce();
|
|
45
|
+
cy.visit(config.registryUrl);
|
|
46
|
+
});
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
cy.task("unpublishPackage", {
|
|
49
|
+
pkgName,
|
|
50
|
+
tempFolder: tempFolder ?? void 0
|
|
51
|
+
});
|
|
52
|
+
if (tempFolder) cy.task("cleanupPublished", tempFolder);
|
|
53
|
+
tempFolder = null;
|
|
54
|
+
});
|
|
55
|
+
it("should have one published package", () => {
|
|
56
|
+
cy.wait("@pkgs");
|
|
57
|
+
cy.getByTestId(pkg.title).should("have.length.at.least", 1);
|
|
58
|
+
});
|
|
59
|
+
it("should navigate to page detail", () => {
|
|
60
|
+
cy.wait("@pkgs");
|
|
61
|
+
cy.wait(300);
|
|
62
|
+
cy.getByTestId(pkg.title).first().click();
|
|
63
|
+
});
|
|
64
|
+
it("should have readme content", () => {
|
|
65
|
+
cy.wait("@pkgs");
|
|
66
|
+
cy.getByTestId(pkg.title).first().click();
|
|
67
|
+
cy.wait("@readme");
|
|
68
|
+
cy.wait("@sidebar");
|
|
69
|
+
cy.getByTestId(pkg.readme).should("be.visible");
|
|
70
|
+
cy.get(markdownBody).should("have.length", 1);
|
|
71
|
+
cy.contains(markdownBody, /test/);
|
|
72
|
+
cy.contains(`${markdownBody} h1`, pkgName).should("be.visible");
|
|
73
|
+
});
|
|
74
|
+
it("should render the sidebar with install commands for npm, yarn, pnpm", () => {
|
|
75
|
+
cy.wait("@pkgs");
|
|
76
|
+
cy.getByTestId(pkg.title).first().click();
|
|
77
|
+
cy.wait("@sidebar");
|
|
78
|
+
cy.getByTestId(pkg.sidebar).should("be.visible");
|
|
79
|
+
cy.getByTestId(pkg.installList).within(() => {
|
|
80
|
+
cy.getByTestId(pkg.installNpm).should("be.visible");
|
|
81
|
+
cy.getByTestId(pkg.installYarn).should("be.visible");
|
|
82
|
+
cy.getByTestId(pkg.installPnpm).should("be.visible");
|
|
83
|
+
});
|
|
84
|
+
cy.getByTestId(pkg.installNpm).should("contain.text", pkgName);
|
|
85
|
+
});
|
|
86
|
+
it("should render the sidebar keywords from the published manifest", () => {
|
|
87
|
+
cy.wait("@pkgs");
|
|
88
|
+
cy.getByTestId(pkg.title).first().click();
|
|
89
|
+
cy.wait("@sidebar");
|
|
90
|
+
cy.getByTestId(pkg.keywordList).should("be.visible");
|
|
91
|
+
cy.getByTestId(pkg.keywordList).should("contain.text", "verdaccio");
|
|
92
|
+
cy.getByTestId(pkg.keywordList).should("contain.text", "e2e");
|
|
93
|
+
cy.getByTestId(pkg.keywordList).should("contain.text", "test");
|
|
94
|
+
});
|
|
95
|
+
it("should click on dependencies tab", () => {
|
|
96
|
+
cy.wait("@pkgs");
|
|
97
|
+
cy.wait(300);
|
|
98
|
+
cy.getByTestId(pkg.title).first().click();
|
|
99
|
+
cy.wait("@readme");
|
|
100
|
+
cy.wait("@sidebar");
|
|
101
|
+
cy.getByTestId(pkg.dependenciesTab).click();
|
|
102
|
+
cy.wait(100);
|
|
103
|
+
cy.getByTestId(pkg.dependencies).should("have.length", 1);
|
|
104
|
+
cy.getByTestId("debug").children().invoke("text").should("match", /debug/);
|
|
105
|
+
});
|
|
106
|
+
it("should click on versions tab", () => {
|
|
107
|
+
cy.wait("@pkgs");
|
|
108
|
+
cy.wait(300);
|
|
109
|
+
cy.getByTestId(pkg.title).first().click();
|
|
110
|
+
cy.wait("@readme");
|
|
111
|
+
cy.wait("@sidebar");
|
|
112
|
+
cy.getByTestId(pkg.versionsTab).click();
|
|
113
|
+
cy.getByTestId(pkg.tagLatest).children().invoke("text").should("match", /1\.0\.0/);
|
|
114
|
+
});
|
|
115
|
+
it("should click on uplinks tab", () => {
|
|
116
|
+
cy.wait("@pkgs");
|
|
117
|
+
cy.wait(300);
|
|
118
|
+
cy.getByTestId(pkg.title).first().click();
|
|
119
|
+
cy.wait("@readme");
|
|
120
|
+
cy.wait("@sidebar");
|
|
121
|
+
cy.getByTestId(pkg.uplinksTab).click();
|
|
122
|
+
cy.getByTestId(pkg.noUplinks).should("be.visible");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { publishTests };
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","names":[],"sources":["../../../src/tests/publish.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\n\nimport { RegistryConfig } from '../types';\n\nexport function publishTests(config: RegistryConfig) {\n const { header, package: pkg } = config.testIds;\n const { markdownBody, loginDialog } = config.selectors;\n\n describe('publish', () => {\n const pkgName = '@verdaccio/pkg-scoped';\n // Per-test state so afterEach can clean up the specific publish\n // that this test created (temp folder + registry entry).\n let tempFolder: string | null = null;\n\n /**\n * Log in once, reuse the session across every test in this suite.\n * `cy.session` caches cookies + localStorage keyed on the first\n * argument, so subsequent calls restore without hitting the network.\n */\n const loginOnce = () => {\n cy.session(\n ['publish-suite', config.credentials.user],\n () => {\n cy.visit(config.registryUrl);\n cy.login(config.credentials.user, config.credentials.password, {\n loginButton: header.loginButton,\n ...loginDialog,\n });\n cy.wait('@sign');\n },\n {\n validate() {\n cy.request({\n url: `${config.registryUrl}/-/verdaccio/data/packages`,\n failOnStatusCode: false,\n })\n .its('status')\n .should('be.oneOf', [200, 304]);\n },\n cacheAcrossSpecs: true,\n }\n );\n };\n\n beforeEach(() => {\n cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');\n cy.intercept('GET', '/-/verdaccio/data/packages').as('pkgs');\n cy.intercept('GET', `/-/verdaccio/data/sidebar/${pkgName}`).as('sidebar');\n cy.intercept('GET', `/-/verdaccio/data/package/readme/${pkgName}`).as('readme');\n\n // Publish a fresh copy for this test. `unique: true` appends a\n // timestamp suffix so the version is distinct per test even when\n // the registry briefly has a stale copy from a prior afterEach.\n cy.task('publishPackage', {\n pkgName,\n version: '1.0.0',\n dependencies: { debug: '4.0.0' },\n unique: true,\n }).then((result) => {\n tempFolder = result?.tempFolder ?? null;\n });\n\n loginOnce();\n cy.visit(config.registryUrl);\n });\n\n afterEach(() => {\n // Remove the package from the registry AND the local temp folder.\n // Run both regardless of test outcome so the next test starts\n // clean. `unpublishPackage` treats 404 as success, so a re-run\n // after a half-published state is still safe.\n cy.task('unpublishPackage', { pkgName, tempFolder: tempFolder ?? undefined });\n if (tempFolder) {\n cy.task('cleanupPublished', tempFolder);\n }\n tempFolder = null;\n });\n\n it('should have one published package', () => {\n cy.wait('@pkgs');\n cy.getByTestId(pkg.title).should('have.length.at.least', 1);\n });\n\n it('should navigate to page detail', () => {\n cy.wait('@pkgs');\n cy.wait(300);\n cy.getByTestId(pkg.title).first().click();\n });\n\n it('should have readme content', () => {\n cy.wait('@pkgs');\n cy.getByTestId(pkg.title).first().click();\n cy.wait('@readme');\n cy.wait('@sidebar');\n cy.getByTestId(pkg.readme).should('be.visible');\n cy.get(markdownBody).should('have.length', 1);\n // publishPackage writes a README whose body contains \"e2e testing\".\n cy.contains(markdownBody, /test/);\n cy.contains(`${markdownBody} h1`, pkgName).should('be.visible');\n });\n\n it('should render the sidebar with install commands for npm, yarn, pnpm', () => {\n cy.wait('@pkgs');\n cy.getByTestId(pkg.title).first().click();\n cy.wait('@sidebar');\n cy.getByTestId(pkg.sidebar).should('be.visible');\n cy.getByTestId(pkg.installList).within(() => {\n cy.getByTestId(pkg.installNpm).should('be.visible');\n cy.getByTestId(pkg.installYarn).should('be.visible');\n cy.getByTestId(pkg.installPnpm).should('be.visible');\n });\n cy.getByTestId(pkg.installNpm).should('contain.text', pkgName);\n });\n\n it('should render the sidebar keywords from the published manifest', () => {\n cy.wait('@pkgs');\n cy.getByTestId(pkg.title).first().click();\n cy.wait('@sidebar');\n // publishPackage writes `keywords: ['verdaccio', 'e2e', 'test']`\n // into the generated package.json.\n cy.getByTestId(pkg.keywordList).should('be.visible');\n cy.getByTestId(pkg.keywordList).should('contain.text', 'verdaccio');\n cy.getByTestId(pkg.keywordList).should('contain.text', 'e2e');\n cy.getByTestId(pkg.keywordList).should('contain.text', 'test');\n });\n\n it('should click on dependencies tab', () => {\n cy.wait('@pkgs');\n cy.wait(300);\n cy.getByTestId(pkg.title).first().click();\n cy.wait('@readme');\n cy.wait('@sidebar');\n cy.getByTestId(pkg.dependenciesTab).click();\n cy.wait(100);\n cy.getByTestId(pkg.dependencies).should('have.length', 1);\n // The dep is rendered with its name as testid. This is a dynamic\n // Verdaccio convention (not configurable via testIds map).\n cy.getByTestId('debug').children().invoke('text').should('match', /debug/);\n });\n\n it('should click on versions tab', () => {\n cy.wait('@pkgs');\n cy.wait(300);\n cy.getByTestId(pkg.title).first().click();\n cy.wait('@readme');\n cy.wait('@sidebar');\n cy.getByTestId(pkg.versionsTab).click();\n // With `unique: true` the version becomes `1.0.0-t<timestamp>`,\n // but \"1.0.0\" still appears as a substring — match loosely.\n cy.getByTestId(pkg.tagLatest)\n .children()\n .invoke('text')\n .should('match', /1\\.0\\.0/);\n });\n\n it('should click on uplinks tab', () => {\n cy.wait('@pkgs');\n cy.wait(300);\n cy.getByTestId(pkg.title).first().click();\n cy.wait('@readme');\n cy.wait('@sidebar');\n cy.getByTestId(pkg.uplinksTab).click();\n cy.getByTestId(pkg.noUplinks).should('be.visible');\n });\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/tests/search.ts
|
|
2
|
+
/**
|
|
3
|
+
* UI tests for the Verdaccio search box.
|
|
4
|
+
*
|
|
5
|
+
* These tests do NOT depend on any published package — they assert the
|
|
6
|
+
* search *flow* (input exists, typing triggers the search API, results
|
|
7
|
+
* region updates) rather than specific package metadata. Tests that need
|
|
8
|
+
* a real package in the registry should live in publishTests instead.
|
|
9
|
+
*/
|
|
10
|
+
function searchTests(config) {
|
|
11
|
+
describe("search", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
cy.intercept("GET", "**/-/verdaccio/data/search/**").as("webSearch");
|
|
14
|
+
cy.intercept("GET", "**/-/v1/search**").as("v1Search");
|
|
15
|
+
cy.intercept("GET", "**/-/verdaccio/data/packages").as("pkgs");
|
|
16
|
+
cy.visit(config.registryUrl);
|
|
17
|
+
cy.get("body").should("be.visible");
|
|
18
|
+
});
|
|
19
|
+
it("should render the search input", () => {
|
|
20
|
+
getSearchInput().should("be.visible");
|
|
21
|
+
});
|
|
22
|
+
it("should fire a search request when typing a query", () => {
|
|
23
|
+
const query = "verdaccio";
|
|
24
|
+
getSearchInput().clear().type(query, { delay: 30 });
|
|
25
|
+
cy.wait(anySearchAlias(), { timeout: 1e4 }).then((interception) => {
|
|
26
|
+
expect(interception.request.url).to.contain(query);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it("should show a \"no results\" state for an impossible query", () => {
|
|
30
|
+
const query = "xyzzy-no-such-package-" + Date.now();
|
|
31
|
+
getSearchInput().clear().type(query, { delay: 30 });
|
|
32
|
+
cy.wait(anySearchAlias(), { timeout: 1e4 });
|
|
33
|
+
cy.wait(300);
|
|
34
|
+
cy.contains(/no\s+(match|results|packages)/i, { timeout: 5e3 }).should("be.visible");
|
|
35
|
+
});
|
|
36
|
+
it("should clear the query and allow typing a new one", () => {
|
|
37
|
+
getSearchInput().clear().type("first-query", { delay: 20 });
|
|
38
|
+
cy.wait(anySearchAlias(), { timeout: 1e4 });
|
|
39
|
+
getSearchInput().clear();
|
|
40
|
+
getSearchInput().should("have.value", "");
|
|
41
|
+
getSearchInput().type("second-query", { delay: 20 });
|
|
42
|
+
cy.wait(anySearchAlias(), { timeout: 1e4 }).then((interception) => {
|
|
43
|
+
expect(interception.request.url).to.contain("second-query");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the search input. Verdaccio 6 renders it with different
|
|
50
|
+
* data-testid values across versions, so we try a few in order before
|
|
51
|
+
* falling back to a generic role/type selector.
|
|
52
|
+
*/
|
|
53
|
+
function getSearchInput() {
|
|
54
|
+
return cy.get([
|
|
55
|
+
"[data-testid=\"search-input\"]",
|
|
56
|
+
"[data-testid=\"header--input-search\"]",
|
|
57
|
+
"[data-testid=\"autoCompleteSearch\"] input",
|
|
58
|
+
"input[aria-label*=\"earch\"]",
|
|
59
|
+
"input[placeholder*=\"earch\"]",
|
|
60
|
+
"input[type=\"search\"]"
|
|
61
|
+
].join(", "), { timeout: 1e4 });
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Cypress `cy.wait` only accepts one alias at a time, so pick whichever
|
|
65
|
+
* alias fires first. This helper lets us stay agnostic to which search
|
|
66
|
+
* endpoint the UI is wired to.
|
|
67
|
+
*/
|
|
68
|
+
function anySearchAlias() {
|
|
69
|
+
return "@webSearch";
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { searchTests };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","names":[],"sources":["../../../src/tests/search.ts"],"sourcesContent":["/// <reference types=\"cypress\" />\n\nimport { RegistryConfig } from '../types';\n\n/**\n * UI tests for the Verdaccio search box.\n *\n * These tests do NOT depend on any published package — they assert the\n * search *flow* (input exists, typing triggers the search API, results\n * region updates) rather than specific package metadata. Tests that need\n * a real package in the registry should live in publishTests instead.\n */\nexport function searchTests(config: RegistryConfig) {\n describe('search', () => {\n beforeEach(() => {\n // Verdaccio's search endpoint — covers both the web API and the\n // npm v1 search API, depending on which one the UI calls.\n cy.intercept('GET', '**/-/verdaccio/data/search/**').as('webSearch');\n cy.intercept('GET', '**/-/v1/search**').as('v1Search');\n cy.intercept('GET', '**/-/verdaccio/data/packages').as('pkgs');\n\n cy.visit(config.registryUrl);\n cy.get('body').should('be.visible');\n });\n\n it('should render the search input', () => {\n getSearchInput().should('be.visible');\n });\n\n it('should fire a search request when typing a query', () => {\n const query = 'verdaccio';\n\n getSearchInput().clear().type(query, { delay: 30 });\n\n // Whichever endpoint the UI is wired to, at least one should hit.\n cy.wait(anySearchAlias(), { timeout: 10000 }).then((interception: any) => {\n expect(interception.request.url).to.contain(query);\n });\n });\n\n it('should show a \"no results\" state for an impossible query', () => {\n const query = 'xyzzy-no-such-package-' + Date.now();\n\n getSearchInput().clear().type(query, { delay: 30 });\n cy.wait(anySearchAlias(), { timeout: 10000 });\n\n // Give the UI a tick to render the empty state.\n cy.wait(300);\n\n // The UI may render \"No Match\", \"No results\", or similar — match\n // loosely rather than binding to one exact string.\n cy.contains(/no\\s+(match|results|packages)/i, { timeout: 5000 }).should(\n 'be.visible'\n );\n });\n\n it('should clear the query and allow typing a new one', () => {\n getSearchInput().clear().type('first-query', { delay: 20 });\n cy.wait(anySearchAlias(), { timeout: 10000 });\n\n // Clearing should empty the input value. We deliberately do NOT\n // assert on the autocomplete dropdown's empty-state text: on a\n // registry with no packages published it lingers regardless of\n // the input value, which previously caused a false positive.\n getSearchInput().clear();\n getSearchInput().should('have.value', '');\n\n // Typing a fresh query must fire another search request so the\n // search box is still functional after a clear.\n getSearchInput().type('second-query', { delay: 20 });\n cy.wait(anySearchAlias(), { timeout: 10000 }).then(\n (interception: any) => {\n expect(interception.request.url).to.contain('second-query');\n }\n );\n });\n });\n}\n\n/**\n * Resolve the search input. Verdaccio 6 renders it with different\n * data-testid values across versions, so we try a few in order before\n * falling back to a generic role/type selector.\n */\nfunction getSearchInput() {\n return cy.get(\n [\n '[data-testid=\"search-input\"]',\n '[data-testid=\"header--input-search\"]',\n '[data-testid=\"autoCompleteSearch\"] input',\n 'input[aria-label*=\"earch\"]',\n 'input[placeholder*=\"earch\"]',\n 'input[type=\"search\"]',\n ].join(', '),\n { timeout: 10000 }\n );\n}\n\n/**\n * Cypress `cy.wait` only accepts one alias at a time, so pick whichever\n * alias fires first. This helper lets us stay agnostic to which search\n * endpoint the UI is wired to.\n */\nfunction anySearchAlias(): string {\n // In practice most Verdaccio 6 builds call the web search endpoint;\n // prefer that one and let the test fail loud if neither fires.\n return '@webSearch';\n}\n"],"mappings":";;;;;;;;;AAYA,SAAgB,YAAY,QAAwB;AAClD,UAAS,gBAAgB;AACvB,mBAAiB;AAGf,MAAG,UAAU,OAAO,gCAAgC,CAAC,GAAG,YAAY;AACpE,MAAG,UAAU,OAAO,mBAAmB,CAAC,GAAG,WAAW;AACtD,MAAG,UAAU,OAAO,+BAA+B,CAAC,GAAG,OAAO;AAE9D,MAAG,MAAM,OAAO,YAAY;AAC5B,MAAG,IAAI,OAAO,CAAC,OAAO,aAAa;IACnC;AAEF,KAAG,wCAAwC;AACzC,mBAAgB,CAAC,OAAO,aAAa;IACrC;AAEF,KAAG,0DAA0D;GAC3D,MAAM,QAAQ;AAEd,mBAAgB,CAAC,OAAO,CAAC,KAAK,OAAO,EAAE,OAAO,IAAI,CAAC;AAGnD,MAAG,KAAK,gBAAgB,EAAE,EAAE,SAAS,KAAO,CAAC,CAAC,MAAM,iBAAsB;AACxE,WAAO,aAAa,QAAQ,IAAI,CAAC,GAAG,QAAQ,MAAM;KAClD;IACF;AAEF,KAAG,oEAAkE;GACnE,MAAM,QAAQ,2BAA2B,KAAK,KAAK;AAEnD,mBAAgB,CAAC,OAAO,CAAC,KAAK,OAAO,EAAE,OAAO,IAAI,CAAC;AACnD,MAAG,KAAK,gBAAgB,EAAE,EAAE,SAAS,KAAO,CAAC;AAG7C,MAAG,KAAK,IAAI;AAIZ,MAAG,SAAS,kCAAkC,EAAE,SAAS,KAAM,CAAC,CAAC,OAC/D,aACD;IACD;AAEF,KAAG,2DAA2D;AAC5D,mBAAgB,CAAC,OAAO,CAAC,KAAK,eAAe,EAAE,OAAO,IAAI,CAAC;AAC3D,MAAG,KAAK,gBAAgB,EAAE,EAAE,SAAS,KAAO,CAAC;AAM7C,mBAAgB,CAAC,OAAO;AACxB,mBAAgB,CAAC,OAAO,cAAc,GAAG;AAIzC,mBAAgB,CAAC,KAAK,gBAAgB,EAAE,OAAO,IAAI,CAAC;AACpD,MAAG,KAAK,gBAAgB,EAAE,EAAE,SAAS,KAAO,CAAC,CAAC,MAC3C,iBAAsB;AACrB,WAAO,aAAa,QAAQ,IAAI,CAAC,GAAG,QAAQ,eAAe;KAE9D;IACD;GACF;;;;;;;AAQJ,SAAS,iBAAiB;AACxB,QAAO,GAAG,IACR;EACE;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK,EACZ,EAAE,SAAS,KAAO,CACnB;;;;;;;AAQH,SAAS,iBAAyB;AAGhC,QAAO"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/tests/settings.ts
|
|
2
|
+
/**
|
|
3
|
+
* Tests for the header Settings dialog and the Translations (language)
|
|
4
|
+
* picker inside it.
|
|
5
|
+
*
|
|
6
|
+
* Requires `web.showSettings: true` in the registry config — the gear
|
|
7
|
+
* icon only renders when the flag is on (see HeaderRight in Verdaccio's
|
|
8
|
+
* ui-components package).
|
|
9
|
+
*
|
|
10
|
+
* Selector strategy: the language cards in LanguageSwitch do NOT have
|
|
11
|
+
* data-testids, so this suite leans on:
|
|
12
|
+
* - MUI's ARIA roles: `[role="dialog"]`, `[role="tab"]`
|
|
13
|
+
* - The stable English labels "Configuration", "Translations",
|
|
14
|
+
* "English", "German", and the language description text, which are
|
|
15
|
+
* bundled into the ui-theme via `@verdaccio/ui-i18n`
|
|
16
|
+
* - The settings trigger's testid from Verdaccio's HeaderToolTipIcon:
|
|
17
|
+
* `header--tooltip-settings`
|
|
18
|
+
*/
|
|
19
|
+
function settingsTests(config) {
|
|
20
|
+
const { header } = config.testIds;
|
|
21
|
+
describe("settings & language", () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
cy.visit(config.registryUrl);
|
|
24
|
+
cy.get("body").should("be.visible");
|
|
25
|
+
});
|
|
26
|
+
it("should open the settings dialog from the header", () => {
|
|
27
|
+
cy.getByTestId(header.settingsTooltip).click();
|
|
28
|
+
cy.get("[role=\"dialog\"]").should("be.visible");
|
|
29
|
+
cy.contains("[role=\"dialog\"]", "Configuration").should("be.visible");
|
|
30
|
+
});
|
|
31
|
+
it("should show both Package Managers and Translations tabs", () => {
|
|
32
|
+
cy.getByTestId(header.settingsTooltip).click();
|
|
33
|
+
cy.get("[role=\"dialog\"]").should("be.visible");
|
|
34
|
+
cy.get("[role=\"dialog\"] [role=\"tab\"]").should("have.length", 2);
|
|
35
|
+
cy.contains("[role=\"dialog\"] [role=\"tab\"]", "Translations").should("be.visible");
|
|
36
|
+
});
|
|
37
|
+
it("should switch to the Translations tab and list languages", () => {
|
|
38
|
+
cy.getByTestId(header.settingsTooltip).click();
|
|
39
|
+
cy.get("[role=\"dialog\"]").should("be.visible");
|
|
40
|
+
cy.contains("[role=\"dialog\"] [role=\"tab\"]", "Translations").click();
|
|
41
|
+
cy.contains("[role=\"dialog\"]", /this is the configuration details for the language/i).should("be.visible");
|
|
42
|
+
cy.contains("[role=\"dialog\"]", "English").should("be.visible");
|
|
43
|
+
cy.contains("[role=\"dialog\"]", "German").should("be.visible");
|
|
44
|
+
});
|
|
45
|
+
it("should change the UI language when a language card is clicked", () => {
|
|
46
|
+
cy.getByTestId(header.settingsTooltip).click();
|
|
47
|
+
cy.get("[role=\"dialog\"]").should("be.visible");
|
|
48
|
+
cy.contains("[role=\"dialog\"] [role=\"tab\"]", "Translations").click();
|
|
49
|
+
cy.contains("[role=\"dialog\"]", "Configuration").invoke("text").then((englishTitle) => {
|
|
50
|
+
cy.contains("[role=\"dialog\"] .MuiCard-root", "German").click({ force: true });
|
|
51
|
+
cy.contains("[role=\"dialog\"] [role=\"tab\"]", "Translations").should("not.exist");
|
|
52
|
+
cy.contains("[role=\"dialog\"]", englishTitle).should("not.exist");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it("should close the settings dialog with Escape", () => {
|
|
56
|
+
cy.getByTestId(header.settingsTooltip).click();
|
|
57
|
+
cy.get("[role=\"dialog\"]").should("be.visible");
|
|
58
|
+
cy.get("body").type("{esc}");
|
|
59
|
+
cy.get("[role=\"dialog\"]").should("not.exist");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { settingsTests };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +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 it('should change the UI language when a language card is clicked', () => {\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 it('should close the settings dialog with Escape', () => {\n cy.getByTestId(header.settingsTooltip).click();\n cy.get('[role=\"dialog\"]').should('be.visible');\n cy.get('body').type('{esc}');\n cy.get('[role=\"dialog\"]').should('not.exist');\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,SAAgB,cAAc,QAAwB;CACpD,MAAM,EAAE,WAAW,OAAO;AAE1B,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,KAAG,uEAAuE;AACxE,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;IACJ;AAEF,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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/tests/signin.ts
|
|
2
|
+
function signinTests(config) {
|
|
3
|
+
const { header } = config.testIds;
|
|
4
|
+
const { loginDialog } = config.selectors;
|
|
5
|
+
describe("sign in / sign out", () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
cy.intercept("POST", "/-/verdaccio/sec/login").as("sign");
|
|
8
|
+
});
|
|
9
|
+
it("should login user", () => {
|
|
10
|
+
cy.visit(config.registryUrl);
|
|
11
|
+
cy.login(config.credentials.user, config.credentials.password, {
|
|
12
|
+
loginButton: header.loginButton,
|
|
13
|
+
...loginDialog
|
|
14
|
+
});
|
|
15
|
+
cy.wait("@sign");
|
|
16
|
+
cy.getByTestId(header.logInDialogIcon).click();
|
|
17
|
+
cy.wait(100);
|
|
18
|
+
cy.getByTestId(header.greetingsLabel).contains(config.credentials.user);
|
|
19
|
+
});
|
|
20
|
+
it("should logout a user", () => {
|
|
21
|
+
cy.visit(config.registryUrl);
|
|
22
|
+
cy.login(config.credentials.user, config.credentials.password, {
|
|
23
|
+
loginButton: header.loginButton,
|
|
24
|
+
...loginDialog
|
|
25
|
+
});
|
|
26
|
+
cy.wait("@sign");
|
|
27
|
+
cy.getByTestId(header.logInDialogIcon).click();
|
|
28
|
+
cy.wait(100);
|
|
29
|
+
cy.getByTestId(header.logOutDialogIcon).click();
|
|
30
|
+
cy.wait(200);
|
|
31
|
+
cy.getByTestId(header.loginButton).contains("Login");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { signinTests };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=signin.js.map
|
|
@@ -0,0 +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.visit(config.registryUrl);\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.visit(config.registryUrl);\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}\n"],"mappings":";AAIA,SAAgB,YAAY,QAAwB;CAClD,MAAM,EAAE,WAAW,OAAO;CAC1B,MAAM,EAAE,gBAAgB,OAAO;AAE/B,UAAS,4BAA4B;AACnC,mBAAiB;AACf,MAAG,UAAU,QAAQ,yBAAyB,CAAC,GAAG,OAAO;IACzD;AAEF,KAAG,2BAA2B;AAC5B,MAAG,MAAM,OAAO,YAAY;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;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,iBAAiB,CAAC,OAAO;AAC/C,MAAG,KAAK,IAAI;AACZ,MAAG,YAAY,OAAO,YAAY,CAAC,SAAS,QAAQ;IACpD;GACF"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
import { RegistryConfig, VerdaccioUiOptions } from './types';
|
|
3
|
+
export type { RegistryConfig, RegistryTaskResult, VerdaccioUiOptions, } from './types';
|
|
4
|
+
export type { PublishPackageInput, PublishPackageTaskInput, PublishPackageResult, UnpublishPackageInput, UnpublishPackageResult, } from './tasks';
|
|
5
|
+
export type { DeepPartial, Selectors, TestIds } from './testIds';
|
|
6
|
+
export { DEFAULT_SELECTORS, DEFAULT_TEST_IDS } from './testIds';
|
|
7
|
+
export { publishPackage, cleanupPublished, unpublishPackage } from './tasks';
|
|
8
|
+
export { homeTests, signinTests, publishTests, searchTests, settingsTests, layoutTests, } from './tests';
|
|
9
|
+
/**
|
|
10
|
+
* Build a full RegistryConfig from user-provided options with defaults.
|
|
11
|
+
*
|
|
12
|
+
* `testIds` and `selectors` are deep-merged per section with the
|
|
13
|
+
* defaults in `./testIds`. Consumers targeting a non-default Verdaccio
|
|
14
|
+
* build can override just the fields that drifted:
|
|
15
|
+
*
|
|
16
|
+
* createRegistryConfig({
|
|
17
|
+
* registryUrl: 'http://localhost:4873',
|
|
18
|
+
* testIds: {
|
|
19
|
+
* header: { settingsTooltip: 'my-new-settings-btn' },
|
|
20
|
+
* },
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
export declare function createRegistryConfig(options: VerdaccioUiOptions): RegistryConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Register Verdaccio Cypress tasks in setupNodeEvents.
|
|
26
|
+
*
|
|
27
|
+
* Usage in cypress.config.ts:
|
|
28
|
+
* import { setupVerdaccioTasks } from '@verdaccio/e2e-ui';
|
|
29
|
+
*
|
|
30
|
+
* export default defineConfig({
|
|
31
|
+
* e2e: {
|
|
32
|
+
* setupNodeEvents(on) {
|
|
33
|
+
* setupVerdaccioTasks(on, { registryUrl: 'http://localhost:4873' });
|
|
34
|
+
* },
|
|
35
|
+
* },
|
|
36
|
+
* });
|
|
37
|
+
*/
|
|
38
|
+
export declare function setupVerdaccioTasks(on: Cypress.PluginEvents, options: VerdaccioUiOptions): void;
|
|
39
|
+
/**
|
|
40
|
+
* Register all Verdaccio UI tests.
|
|
41
|
+
*
|
|
42
|
+
* Usage in a spec file:
|
|
43
|
+
* import { registerAllTests, createRegistryConfig } from '@verdaccio/e2e-ui';
|
|
44
|
+
*
|
|
45
|
+
* const config = createRegistryConfig({ registryUrl: 'http://localhost:4873' });
|
|
46
|
+
* registerAllTests(config);
|
|
47
|
+
*
|
|
48
|
+
* Or pick individual suites:
|
|
49
|
+
* import { homeTests, signinTests } from '@verdaccio/e2e-ui';
|
|
50
|
+
*
|
|
51
|
+
* const config = createRegistryConfig({ registryUrl: 'http://localhost:4873' });
|
|
52
|
+
* homeTests(config);
|
|
53
|
+
* signinTests(config);
|
|
54
|
+
*/
|
|
55
|
+
export declare function registerAllTests(config: RegistryConfig): void;
|