kunai-runner 6.10.101
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/.env.example +68 -0
- package/README.md +17 -0
- package/docs/01_shibboleth_auth.md +122 -0
- package/docs/02_versioning_and_release.md +146 -0
- package/docs/backlog.md +71 -0
- package/docs/combine_videos.md +109 -0
- package/docs/test_data.md +34 -0
- package/lib/auth-file.ts +22 -0
- package/lib/login-adapters/builtin.ts +42 -0
- package/lib/login-adapters/duo-mfa.ts +41 -0
- package/lib/login-adapters/incommon-seamlessaccess.ts +56 -0
- package/lib/login-adapters/index.ts +68 -0
- package/lib/login-adapters/shibboleth-direct.ts +34 -0
- package/lib/login-adapters/types.ts +21 -0
- package/package.json +51 -0
- package/playwright.config.ts +206 -0
- package/scripts/combine_videos.py +250 -0
- package/tests/suite/01-preflight.spec.ts +147 -0
- package/tests/suite/02-account-management.spec.ts +113 -0
- package/tests/suite/03-create-dataverse.spec.ts +114 -0
- package/tests/suite/04-publish-dataverse.spec.ts +33 -0
- package/tests/suite/05-theme-widgets.spec.ts +65 -0
- package/tests/suite/06-theme-widgets-edit.spec.ts +60 -0
- package/tests/suite/10-assign-user-group-roles.spec.ts +34 -0
- package/tests/suite/11-create-edit-metadata-template.spec.ts +61 -0
- package/tests/suite/12-create-dataverse-collection.spec.ts +27 -0
- package/tests/suite/13-dataset-actions.spec.ts +105 -0
- package/tests/suite/14-browse-dataset-records.spec.ts +32 -0
- package/tests/suite/15-search-dataset-records.spec.ts +26 -0
- package/tests/suite/16-view-dataset-version-history.spec.ts +28 -0
- package/tests/suite/17-download-dataset-files.spec.ts +35 -0
- package/tests/suite/assets/footer.png +0 -0
- package/tests/suite/assets/logo.png +0 -0
- package/tests/suite/assets/thumbnail.png +0 -0
- package/tests/suite/auth.setup.ts +71 -0
- package/tests/suite/s02-state.ts +35 -0
- package/tests/suite/test-data/replaced-sample-dataset-file.txt +7 -0
- package/tests/suite/test-data/sample-dataset-file-2.txt +7 -0
- package/tests/suite/test-data/sample-dataset-file.txt +7 -0
- package/tests/suite/tsconfig.json +10 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
const process = (globalThis as any).process;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @tags @standard
|
|
6
|
+
*
|
|
7
|
+
* Preflight health-checks for a standard UNC Dataverse instance.
|
|
8
|
+
* Verifies the home page, header nav, and footer before any auth runs.
|
|
9
|
+
* Skip this test (--grep-invert @standard) when targeting a 21 CFR instance.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
async function followLinkInPlace(
|
|
13
|
+
page: import("@playwright/test").Page,
|
|
14
|
+
linkLocator: import("@playwright/test").Locator,
|
|
15
|
+
assertions: (page: import("@playwright/test").Page) => Promise<void>,
|
|
16
|
+
) {
|
|
17
|
+
const href = await linkLocator.getAttribute("href");
|
|
18
|
+
if (!href) throw new Error("Link has no href");
|
|
19
|
+
const from = page.url();
|
|
20
|
+
await page.goto(href);
|
|
21
|
+
await page.waitForLoadState("domcontentloaded");
|
|
22
|
+
await assertions(page);
|
|
23
|
+
await page.goto(from);
|
|
24
|
+
await page.waitForLoadState("domcontentloaded");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test("Preflight", { tag: ["@standard"] }, async ({ page }) => {
|
|
28
|
+
if (process.env.SKIP_PREFLIGHT === "true") return;
|
|
29
|
+
await page.goto("/");
|
|
30
|
+
|
|
31
|
+
// ─────────────────────────────────────────────
|
|
32
|
+
// HEADER
|
|
33
|
+
// ─────────────────────────────────────────────
|
|
34
|
+
const navbar = page.locator("#navbarFixed");
|
|
35
|
+
|
|
36
|
+
// 1. UNC Dataverse logo loads and is visible
|
|
37
|
+
const logo = navbar.locator('img[alt="UNC Dataverse homepage"].custom-logo');
|
|
38
|
+
await expect(logo).toBeVisible();
|
|
39
|
+
const logoLoaded = await logo.evaluate(
|
|
40
|
+
(img: HTMLImageElement) => img.naturalWidth > 0,
|
|
41
|
+
);
|
|
42
|
+
expect(logoLoaded).toBe(true);
|
|
43
|
+
|
|
44
|
+
// 2. Search returns results for "unc"
|
|
45
|
+
await navbar.getByRole("link", { name: "Search" }).click();
|
|
46
|
+
await navbar.locator("#navbarsearch").type("unc");
|
|
47
|
+
await navbar
|
|
48
|
+
.locator('button[type="submit"][aria-labelledby="searchNavLabel"]')
|
|
49
|
+
.click();
|
|
50
|
+
await expect(page).toHaveURL(/\/dataverse\/unc\?q=unc/);
|
|
51
|
+
const rows = page.locator("#searchResults #dv-main #resultsTable tbody tr");
|
|
52
|
+
await expect(rows.first()).toBeVisible();
|
|
53
|
+
expect(await rows.count()).toBeGreaterThan(0);
|
|
54
|
+
await page.goto("/");
|
|
55
|
+
|
|
56
|
+
// 3. About → tdx.unc.edu, mentions "Dataverse"
|
|
57
|
+
await followLinkInPlace(
|
|
58
|
+
page,
|
|
59
|
+
navbar.getByRole("link", { name: "About" }),
|
|
60
|
+
async (p) => {
|
|
61
|
+
await expect(p).toHaveURL(/tdx\.unc\.edu/);
|
|
62
|
+
await expect(p.getByText(/Dataverse/i).first()).toBeVisible();
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// 4. User Guide → guides.dataverse.org, heading "User Guide" visible
|
|
67
|
+
await followLinkInPlace(
|
|
68
|
+
page,
|
|
69
|
+
navbar.getByRole("link", { name: "User Guide" }),
|
|
70
|
+
async (p) => {
|
|
71
|
+
await expect(p).toHaveURL(/guides\.dataverse\.org/);
|
|
72
|
+
await expect(
|
|
73
|
+
p.getByRole("heading", { name: "User Guide" }),
|
|
74
|
+
).toBeVisible();
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// 5. Support → tdx.unc.edu, mentions "Dataverse"
|
|
79
|
+
await followLinkInPlace(
|
|
80
|
+
page,
|
|
81
|
+
navbar.getByRole("link", { name: "Support" }),
|
|
82
|
+
async (p) => {
|
|
83
|
+
await expect(p).toHaveURL(/tdx\.unc\.edu/);
|
|
84
|
+
await expect(p.getByText(/Dataverse/i).first()).toBeVisible();
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// ─────────────────────────────────────────────
|
|
89
|
+
// FOOTER
|
|
90
|
+
// ─────────────────────────────────────────────
|
|
91
|
+
const footer = page.locator(".custom-footer");
|
|
92
|
+
|
|
93
|
+
// 1. RDMC logo loads and is visible
|
|
94
|
+
const footerLogo = footer.locator('img[alt="UNC Dataverse"]');
|
|
95
|
+
await expect(footerLogo).toBeVisible();
|
|
96
|
+
const footerLogoLoaded = await footerLogo.evaluate(
|
|
97
|
+
(img: HTMLImageElement) => img.naturalWidth > 0,
|
|
98
|
+
);
|
|
99
|
+
expect(footerLogoLoaded).toBe(true);
|
|
100
|
+
|
|
101
|
+
// 2. UNC Dataverse User Guide → tdx.unc.edu, has word "Guide"
|
|
102
|
+
await followLinkInPlace(
|
|
103
|
+
page,
|
|
104
|
+
footer.getByRole("link", { name: "UNC Dataverse User Guide" }),
|
|
105
|
+
async (p) => {
|
|
106
|
+
await expect(p).toHaveURL(/tdx\.unc\.edu/);
|
|
107
|
+
await expect(p.getByText(/Guide/i).first()).toBeVisible();
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// 3. UNC Dataverse Terms → tdx.unc.edu, has "Terms of Use"
|
|
112
|
+
await followLinkInPlace(
|
|
113
|
+
page,
|
|
114
|
+
footer.getByRole("link", { name: "UNC Dataverse Terms" }),
|
|
115
|
+
async (p) => {
|
|
116
|
+
await expect(p).toHaveURL(/tdx\.unc\.edu/);
|
|
117
|
+
await expect(p.getByText(/Terms of Use/i).first()).toBeVisible();
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// 4. RDMC Data Services → URL contains "basic" & "services"
|
|
122
|
+
await followLinkInPlace(
|
|
123
|
+
page,
|
|
124
|
+
footer.getByRole("link", { name: "RDMC Data Services" }),
|
|
125
|
+
async (p) => {
|
|
126
|
+
const url = p.url().toLowerCase();
|
|
127
|
+
expect(url).toContain("basic");
|
|
128
|
+
expect(url).toContain("services");
|
|
129
|
+
await expect(p.getByText(/Service/i).first()).toBeVisible();
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// 5. Submit via RDMC service portal → tdx.unc.edu, has "RDMC"
|
|
134
|
+
await followLinkInPlace(
|
|
135
|
+
page,
|
|
136
|
+
footer.getByRole("link", { name: /Submit via RDMC service portal/i }),
|
|
137
|
+
async (p) => {
|
|
138
|
+
await expect(p).toHaveURL(/tdx\.unc\.edu/);
|
|
139
|
+
await expect(p.getByText(/RDMC/i).first()).toBeVisible();
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// 6. Email anchor has a mailto: href (no navigation needed)
|
|
144
|
+
const emailLink = footer.getByRole("link", { name: /Email/i });
|
|
145
|
+
const emailHref = await emailLink.getAttribute("href");
|
|
146
|
+
expect(emailHref?.toLowerCase()).toBe("mailto:rdmcarchive@unc.edu");
|
|
147
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @tags @standard
|
|
5
|
+
*
|
|
6
|
+
* Section 1, Test #2 — Account Management
|
|
7
|
+
* Exercises My Data, Notifications, Account Information, and API Token
|
|
8
|
+
* from the authenticated user dropdown.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
test(
|
|
12
|
+
"Section 1: Account Management",
|
|
13
|
+
{ tag: ["@standard"] },
|
|
14
|
+
async ({ page }) => {
|
|
15
|
+
await page.goto("/");
|
|
16
|
+
|
|
17
|
+
const userMenuTrigger = page.locator("span#userDisplayInfoTitle");
|
|
18
|
+
|
|
19
|
+
// ─────────────────────────────────────────────
|
|
20
|
+
// 1. My Data
|
|
21
|
+
// ─────────────────────────────────────────────
|
|
22
|
+
await userMenuTrigger.click();
|
|
23
|
+
const dropdownMenu = page
|
|
24
|
+
.locator("ul.dropdown-menu")
|
|
25
|
+
.filter({ has: page.getByRole("link", { name: "Log Out" }) });
|
|
26
|
+
await expect(dropdownMenu).toBeVisible();
|
|
27
|
+
|
|
28
|
+
await dropdownMenu.getByRole("link", { name: "My Data" }).click();
|
|
29
|
+
await expect(page).toHaveURL(
|
|
30
|
+
/dataverseuser\.xhtml\?selectTab=dataRelatedToMe/,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const cardResults = page.locator("#resultsTable #div-card-results");
|
|
34
|
+
await expect(cardResults).toBeVisible();
|
|
35
|
+
const subDivs = cardResults.locator("> div");
|
|
36
|
+
expect(await subDivs.count()).toBeGreaterThan(0);
|
|
37
|
+
|
|
38
|
+
await page.goto("/");
|
|
39
|
+
await userMenuTrigger.click();
|
|
40
|
+
await expect(dropdownMenu).toBeVisible();
|
|
41
|
+
|
|
42
|
+
// ─────────────────────────────────────────────
|
|
43
|
+
// 2. Notifications
|
|
44
|
+
// ─────────────────────────────────────────────
|
|
45
|
+
await dropdownMenu.getByRole("link", { name: "Notifications" }).click();
|
|
46
|
+
await expect(page).toHaveURL(
|
|
47
|
+
/dataverseuser\.xhtml\?selectTab=notifications/,
|
|
48
|
+
);
|
|
49
|
+
await expect(
|
|
50
|
+
page.getByRole("link", { name: "Notifications" }).first(),
|
|
51
|
+
).toBeVisible();
|
|
52
|
+
|
|
53
|
+
await page.goto("/");
|
|
54
|
+
await userMenuTrigger.click();
|
|
55
|
+
await expect(dropdownMenu).toBeVisible();
|
|
56
|
+
|
|
57
|
+
// ─────────────────────────────────────────────
|
|
58
|
+
// 3. Account Information
|
|
59
|
+
// ─────────────────────────────────────────────
|
|
60
|
+
await dropdownMenu
|
|
61
|
+
.getByRole("link", { name: "Account Information" })
|
|
62
|
+
.click();
|
|
63
|
+
await expect(page).toHaveURL(/dataverseuser\.xhtml\?selectTab=accountInfo/);
|
|
64
|
+
|
|
65
|
+
const metadataTable = page.locator("table.metadata");
|
|
66
|
+
await expect(metadataTable).toBeVisible();
|
|
67
|
+
|
|
68
|
+
for (const heading of ["Username", "Given Name", "Family Name", "Email"]) {
|
|
69
|
+
await expect(
|
|
70
|
+
metadataTable.getByRole("rowheader", { name: heading }),
|
|
71
|
+
).toBeVisible();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await expect(page.getByText(/Verified/i).first()).toBeVisible();
|
|
75
|
+
|
|
76
|
+
await page.goto("/");
|
|
77
|
+
await userMenuTrigger.click();
|
|
78
|
+
await expect(dropdownMenu).toBeVisible();
|
|
79
|
+
|
|
80
|
+
// ─────────────────────────────────────────────
|
|
81
|
+
// 4. API Token
|
|
82
|
+
// ─────────────────────────────────────────────
|
|
83
|
+
await dropdownMenu.getByRole("link", { name: "API Token" }).click();
|
|
84
|
+
await expect(page).toHaveURL(/dataverseuser\.xhtml\?selectTab=apiTokenTab/);
|
|
85
|
+
|
|
86
|
+
const createTokenBtn = page.getByRole("button", { name: "Create Token" });
|
|
87
|
+
await expect(createTokenBtn).toBeVisible();
|
|
88
|
+
await createTokenBtn.click();
|
|
89
|
+
await page.waitForTimeout(1000);
|
|
90
|
+
|
|
91
|
+
const tokenCode = page.locator("#apiToken pre code");
|
|
92
|
+
await expect(tokenCode).toBeVisible();
|
|
93
|
+
const tokenText = await tokenCode.textContent();
|
|
94
|
+
expect(tokenText).toMatch(/[a-zA-Z]/);
|
|
95
|
+
expect(tokenText).toMatch(/[0-9]/);
|
|
96
|
+
expect(tokenText).toContain("-");
|
|
97
|
+
|
|
98
|
+
await expect(page.getByText(/Expiration Date/i).first()).toBeVisible();
|
|
99
|
+
|
|
100
|
+
const expirationTd = page
|
|
101
|
+
.locator("table.metadata tbody tr")
|
|
102
|
+
.filter({ has: page.locator("th", { hasText: "Expiration Date" }) })
|
|
103
|
+
.locator("td");
|
|
104
|
+
const expirationText = await expirationTd.textContent();
|
|
105
|
+
const currentYear = new Date().getFullYear();
|
|
106
|
+
expect(expirationText).toContain(String(currentYear + 1));
|
|
107
|
+
|
|
108
|
+
await page.getByRole("button", { name: "Revoke Token" }).click();
|
|
109
|
+
await expect(createTokenBtn).toBeVisible();
|
|
110
|
+
|
|
111
|
+
await page.goto("/");
|
|
112
|
+
},
|
|
113
|
+
);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
import { writeDataverseId } from "./s02-state";
|
|
3
|
+
const process = (globalThis as any).process;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @tags @standard
|
|
7
|
+
*
|
|
8
|
+
* Section 2, Test #1 — Dataverse Creation
|
|
9
|
+
* Creates a new child dataverse under ROOT_DATAVERSE and persists its
|
|
10
|
+
* identifier for downstream tests in this section.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
test(
|
|
14
|
+
"Section 2: Create Dataverse",
|
|
15
|
+
{ tag: ["@standard"] },
|
|
16
|
+
async ({ page }) => {
|
|
17
|
+
const suffix = Date.now().toString(36);
|
|
18
|
+
const dataverseIdentifier = `gorilla-tiger-monkey-${suffix}`;
|
|
19
|
+
|
|
20
|
+
const rootDataverse = process.env.ROOT_DATAVERSE ?? "/dataverse/unc";
|
|
21
|
+
await page.goto(rootDataverse);
|
|
22
|
+
|
|
23
|
+
await page.getByRole("button", { name: "Add Data" }).click();
|
|
24
|
+
await page.getByRole("link", { name: "New Dataverse" }).click();
|
|
25
|
+
await page.waitForLoadState("domcontentloaded");
|
|
26
|
+
|
|
27
|
+
// ─── Metadata Fields ───
|
|
28
|
+
const metadataRootChk = page.locator('[id="dataverseForm:metadataRoot"]');
|
|
29
|
+
const optionBlock = page.locator('[id="dataverseForm:optionBlock"]');
|
|
30
|
+
|
|
31
|
+
await expect(metadataRootChk).toBeChecked();
|
|
32
|
+
|
|
33
|
+
const checkboxDivs = optionBlock.locator("div.checkbox");
|
|
34
|
+
const labelCount = await checkboxDivs.count();
|
|
35
|
+
expect(labelCount).toBeGreaterThan(0);
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < labelCount; i++) {
|
|
38
|
+
await expect(
|
|
39
|
+
checkboxDivs.nth(i).locator("label input[type='checkbox']"),
|
|
40
|
+
).toHaveAttribute("disabled");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await metadataRootChk.uncheck();
|
|
44
|
+
|
|
45
|
+
await expect(
|
|
46
|
+
checkboxDivs.nth(0).locator("label input[type='checkbox']"),
|
|
47
|
+
).toHaveAttribute("disabled");
|
|
48
|
+
for (let i = 1; i < labelCount; i++) {
|
|
49
|
+
await expect(
|
|
50
|
+
checkboxDivs.nth(i).locator("label input[type='checkbox']"),
|
|
51
|
+
).not.toHaveAttribute("disabled");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await metadataRootChk.check();
|
|
55
|
+
|
|
56
|
+
const metaContinueBtn = page
|
|
57
|
+
.locator("button:visible")
|
|
58
|
+
.filter({ has: page.locator("span", { hasText: "Continue" }) });
|
|
59
|
+
await expect(metaContinueBtn).toBeVisible();
|
|
60
|
+
await metaContinueBtn.click();
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < labelCount; i++) {
|
|
63
|
+
await expect(
|
|
64
|
+
checkboxDivs.nth(i).locator("label input[type='checkbox']"),
|
|
65
|
+
).toHaveAttribute("disabled");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Browse/Search Facets ───
|
|
69
|
+
const facetsRoot = page.locator('[id="dataverseForm:facetsRoot"]');
|
|
70
|
+
const editFacets = page.locator('[id="dataverseForm:editFacets"]');
|
|
71
|
+
const picklistHeaderInner = editFacets
|
|
72
|
+
.locator("div.ui-picklist-list-wrapper div.ui-widget-header > div")
|
|
73
|
+
.first();
|
|
74
|
+
|
|
75
|
+
await expect(facetsRoot).toBeChecked();
|
|
76
|
+
await expect(picklistHeaderInner).toHaveClass(/ui-state-disabled/);
|
|
77
|
+
|
|
78
|
+
await facetsRoot.uncheck();
|
|
79
|
+
await expect(picklistHeaderInner).not.toHaveClass(/ui-state-disabled/);
|
|
80
|
+
|
|
81
|
+
await facetsRoot.check();
|
|
82
|
+
await expect(picklistHeaderInner).toHaveClass(/ui-state-disabled/);
|
|
83
|
+
|
|
84
|
+
// ─── Fill description ───
|
|
85
|
+
await page
|
|
86
|
+
.locator('[id="dataverseForm:description"]')
|
|
87
|
+
.fill(
|
|
88
|
+
'Welcome to <a href="https://researchdata.unc.edu/"><b>RDMC</b></a>',
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// ─── Fill identifier ───
|
|
92
|
+
await page
|
|
93
|
+
.locator('[id="dataverseForm:identifier"]')
|
|
94
|
+
.fill(dataverseIdentifier);
|
|
95
|
+
|
|
96
|
+
// ─── Category ───
|
|
97
|
+
await page
|
|
98
|
+
.locator('[id="dataverseForm:dataverseCategory"]')
|
|
99
|
+
.selectOption("DEPARTMENT");
|
|
100
|
+
|
|
101
|
+
// ─── Create ───
|
|
102
|
+
await page.getByRole("button", { name: "Create Dataverse" }).click();
|
|
103
|
+
await page.waitForLoadState("domcontentloaded");
|
|
104
|
+
|
|
105
|
+
await expect(page).toHaveURL(
|
|
106
|
+
new RegExp(`/dataverse/${dataverseIdentifier}`),
|
|
107
|
+
);
|
|
108
|
+
await expect(
|
|
109
|
+
page.locator('a[data-original-title="Email Dataverse Contact"]'),
|
|
110
|
+
).toBeVisible();
|
|
111
|
+
|
|
112
|
+
writeDataverseId(dataverseIdentifier);
|
|
113
|
+
},
|
|
114
|
+
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
import { readDataverseId } from "./s02-state";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @tags @standard
|
|
6
|
+
*
|
|
7
|
+
* Section 2, Test #2 — Publish Dataverse
|
|
8
|
+
* Publishes the dataverse created in test 03.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
test(
|
|
12
|
+
"Section 2: Publish Dataverse",
|
|
13
|
+
{ tag: ["@standard"] },
|
|
14
|
+
async ({ page }) => {
|
|
15
|
+
const dataverseIdentifier = readDataverseId();
|
|
16
|
+
|
|
17
|
+
await page.goto(`/dataverse/${dataverseIdentifier}`);
|
|
18
|
+
await page.waitForLoadState("domcontentloaded");
|
|
19
|
+
|
|
20
|
+
await page.getByRole("button", { name: "Publish" }).click();
|
|
21
|
+
|
|
22
|
+
const publishContinueBtn = page
|
|
23
|
+
.locator("button:visible")
|
|
24
|
+
.filter({ has: page.locator("span", { hasText: "Continue" }) });
|
|
25
|
+
await expect(publishContinueBtn).toBeVisible();
|
|
26
|
+
await publishContinueBtn.click();
|
|
27
|
+
|
|
28
|
+
const successAlert = page.locator("div.alert.alert-success");
|
|
29
|
+
await expect(successAlert).toBeVisible();
|
|
30
|
+
await expect(successAlert).toContainText("Success!");
|
|
31
|
+
await expect(successAlert).toContainText("Your dataverse is now public.");
|
|
32
|
+
},
|
|
33
|
+
);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
import { readDataverseId } from "./s02-state";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @tags @standard
|
|
7
|
+
*
|
|
8
|
+
* Section 2, Test #3 — Theme + Widgets (initial setup)
|
|
9
|
+
* Uploads logo, thumbnail, footer images and sets tagline/website URL.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
test(
|
|
13
|
+
"Section 2: Theme + Widgets – initial setup",
|
|
14
|
+
{ tag: ["@standard"] },
|
|
15
|
+
async ({ page }) => {
|
|
16
|
+
const dataverseIdentifier = readDataverseId();
|
|
17
|
+
|
|
18
|
+
await page.goto(`/dataverse/${dataverseIdentifier}`);
|
|
19
|
+
await page.waitForLoadState("domcontentloaded");
|
|
20
|
+
|
|
21
|
+
await page.getByRole("button", { name: /Edit/i }).click();
|
|
22
|
+
await page.getByRole("link", { name: "Theme + Widgets" }).click();
|
|
23
|
+
await page.waitForLoadState("domcontentloaded");
|
|
24
|
+
|
|
25
|
+
// Upload logo.png
|
|
26
|
+
await page
|
|
27
|
+
.locator('[id="themeWidgetsForm:themeWidgetsTabView:uploadlogo_input"]')
|
|
28
|
+
.setInputFiles(path.join(__dirname, "assets", "logo.png"));
|
|
29
|
+
|
|
30
|
+
// Upload thumbnail.png
|
|
31
|
+
await page
|
|
32
|
+
.locator(
|
|
33
|
+
'[id="themeWidgetsForm:themeWidgetsTabView:uploadlogoThumbnail_input"]',
|
|
34
|
+
)
|
|
35
|
+
.setInputFiles(path.join(__dirname, "assets", "thumbnail.png"));
|
|
36
|
+
|
|
37
|
+
// Upload footer.png
|
|
38
|
+
await page
|
|
39
|
+
.locator(
|
|
40
|
+
'[id="themeWidgetsForm:themeWidgetsTabView:uploadlogoFooter_input"]',
|
|
41
|
+
)
|
|
42
|
+
.setInputFiles(path.join(__dirname, "assets", "footer.png"));
|
|
43
|
+
|
|
44
|
+
// Tagline
|
|
45
|
+
await page
|
|
46
|
+
.locator('[id="themeWidgetsForm:themeWidgetsTabView:tagline"]')
|
|
47
|
+
.fill(
|
|
48
|
+
"Please click here to find more details about this automated testing",
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Website URL
|
|
52
|
+
await page
|
|
53
|
+
.locator('[id="themeWidgetsForm:themeWidgetsTabView:website"]')
|
|
54
|
+
.fill("https://www.youtube.com/watch?v=2qBlE2-WL60");
|
|
55
|
+
|
|
56
|
+
await page.getByRole("button", { name: "Save Changes" }).click();
|
|
57
|
+
|
|
58
|
+
const successAlert = page.locator("div.alert.alert-success");
|
|
59
|
+
await expect(successAlert).toBeVisible();
|
|
60
|
+
await expect(successAlert).toContainText("Success!");
|
|
61
|
+
await expect(successAlert).toContainText(
|
|
62
|
+
"You have successfully updated the theme for this dataverse!",
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
import { readDataverseId } from "./s02-state";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @tags @standard
|
|
7
|
+
*
|
|
8
|
+
* Section 2, Test #4 — Theme + Widgets (remove & re-upload)
|
|
9
|
+
* Removes the second uploaded image then re-uploads a replacement thumbnail.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
test(
|
|
13
|
+
"Section 2: Theme + Widgets – remove and re-upload",
|
|
14
|
+
{ tag: ["@standard"] },
|
|
15
|
+
async ({ page }) => {
|
|
16
|
+
const dataverseIdentifier = readDataverseId();
|
|
17
|
+
|
|
18
|
+
await page.goto(`/dataverse/${dataverseIdentifier}`);
|
|
19
|
+
await page.waitForLoadState("domcontentloaded");
|
|
20
|
+
|
|
21
|
+
// ─── Remove second image, Save ───
|
|
22
|
+
await page.getByRole("button", { name: /Edit/i }).click();
|
|
23
|
+
await page.getByRole("link", { name: "Theme + Widgets" }).click();
|
|
24
|
+
await page.waitForLoadState("domcontentloaded");
|
|
25
|
+
|
|
26
|
+
const removeButtons = page
|
|
27
|
+
.locator("button:visible")
|
|
28
|
+
.filter({ has: page.locator("span", { hasText: "Remove" }) });
|
|
29
|
+
await removeButtons.nth(1).click();
|
|
30
|
+
|
|
31
|
+
await page.getByRole("button", { name: "Save Changes" }).click();
|
|
32
|
+
|
|
33
|
+
const successAlert1 = page.locator("div.alert.alert-success");
|
|
34
|
+
await expect(successAlert1).toBeVisible();
|
|
35
|
+
await expect(successAlert1).toContainText("Success!");
|
|
36
|
+
await expect(successAlert1).toContainText(
|
|
37
|
+
"You have successfully updated the theme for this dataverse!",
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// ─── Re-upload thumbnail, Save ───
|
|
41
|
+
await page.getByRole("button", { name: /Edit/i }).click();
|
|
42
|
+
await page.getByRole("link", { name: "Theme + Widgets" }).click();
|
|
43
|
+
await page.waitForLoadState("domcontentloaded");
|
|
44
|
+
|
|
45
|
+
await page
|
|
46
|
+
.locator(
|
|
47
|
+
'[id="themeWidgetsForm:themeWidgetsTabView:uploadlogoThumbnail_input"]',
|
|
48
|
+
)
|
|
49
|
+
.setInputFiles(path.join(__dirname, "assets", "logo.png"));
|
|
50
|
+
|
|
51
|
+
await page.getByRole("button", { name: "Save Changes" }).click();
|
|
52
|
+
|
|
53
|
+
const successAlert2 = page.locator("div.alert.alert-success");
|
|
54
|
+
await expect(successAlert2).toBeVisible();
|
|
55
|
+
await expect(successAlert2).toContainText("Success!");
|
|
56
|
+
await expect(successAlert2).toContainText(
|
|
57
|
+
"You have successfully updated the theme for this dataverse!",
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
const process = (globalThis as any).process;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @tags @21cfr
|
|
6
|
+
*
|
|
7
|
+
* 21 CFR Part 11 — Test #2
|
|
8
|
+
* Assign authenticated users a role on the root dataverse collection, then
|
|
9
|
+
* remove that role assignment.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
test(
|
|
13
|
+
"21 CFR: Assign user group roles",
|
|
14
|
+
{ tag: ["@21cfr"] },
|
|
15
|
+
async ({ page }) => {
|
|
16
|
+
await page.goto(process.env.ROOT_DATAVERSE ?? "/");
|
|
17
|
+
await page.getByText("Edit").click();
|
|
18
|
+
await page.getByText("Permissions").click();
|
|
19
|
+
await page.getByText("Users/Groups All the users").click();
|
|
20
|
+
await page.getByText("Assign Roles to Users/Groups").click();
|
|
21
|
+
await page
|
|
22
|
+
.getByPlaceholder("Enter User/Group Name")
|
|
23
|
+
.type(":authenticated-users");
|
|
24
|
+
await page.waitForTimeout(2000);
|
|
25
|
+
await page.keyboard.press("Enter");
|
|
26
|
+
await page.locator("span.ui-radiobutton-icon").last().click();
|
|
27
|
+
await page.getByRole("button", { name: "Save Changes" }).click();
|
|
28
|
+
await page
|
|
29
|
+
.getByRole("gridcell", { name: /Remove Assigned Role/ })
|
|
30
|
+
.last()
|
|
31
|
+
.click();
|
|
32
|
+
await page.getByText(/Continue/).click();
|
|
33
|
+
},
|
|
34
|
+
);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
const process = (globalThis as any).process;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @tags @21cfr
|
|
6
|
+
*
|
|
7
|
+
* 21 CFR Part 11 — Tests #3 & #4
|
|
8
|
+
* Creates a dataset template, edits its name, then deletes it.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
test(
|
|
12
|
+
"21 CFR: Create and edit metadata template",
|
|
13
|
+
{ tag: ["@21cfr"] },
|
|
14
|
+
async ({ page }) => {
|
|
15
|
+
await page.goto(process.env.ROOT_DATAVERSE ?? "/");
|
|
16
|
+
await page.getByText("Edit").click();
|
|
17
|
+
await page.getByText("Dataset Templates").click();
|
|
18
|
+
await page.getByText("Create Dataset Template").first().click();
|
|
19
|
+
await page
|
|
20
|
+
.locator('[id$=":templateName"]')
|
|
21
|
+
.type("Playwright Test Template");
|
|
22
|
+
const templateFormLocators = await page.locator('[id$=":inputText"]').all();
|
|
23
|
+
await templateFormLocators[0].type("Test Citation");
|
|
24
|
+
await templateFormLocators[6].type("Playwright Auto Tester");
|
|
25
|
+
await templateFormLocators[11].type("tester-dummy@unc.edu");
|
|
26
|
+
await page
|
|
27
|
+
.locator('[id$=":description"]')
|
|
28
|
+
.first()
|
|
29
|
+
.type(
|
|
30
|
+
"This is a dummy template created by Playwright for testing purposes.",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
await page
|
|
34
|
+
.locator(".ui-selectcheckboxmenu-multiple-container")
|
|
35
|
+
.first()
|
|
36
|
+
.click();
|
|
37
|
+
|
|
38
|
+
await page
|
|
39
|
+
.locator(".ui-selectcheckboxmenu-items-wrapper")
|
|
40
|
+
.first()
|
|
41
|
+
.getByText("Chemistry")
|
|
42
|
+
.click();
|
|
43
|
+
|
|
44
|
+
await page.getByRole("button", { name: "Save + Add Terms" }).click();
|
|
45
|
+
await page.getByRole("button", { name: "Save Dataset Template" }).click();
|
|
46
|
+
|
|
47
|
+
await page.getByRole("button", { name: "Edit Template" }).first().click();
|
|
48
|
+
await page.getByRole("link", { name: "Metadata" }).click();
|
|
49
|
+
await page
|
|
50
|
+
.locator('[id$=":templateName"]')
|
|
51
|
+
.fill("Playwright Test Template II");
|
|
52
|
+
await page.getByRole("button", { name: "Save Changes" }).click();
|
|
53
|
+
|
|
54
|
+
await page
|
|
55
|
+
.locator(".btn-group")
|
|
56
|
+
.first()
|
|
57
|
+
.locator('[data-original-title="Delete"]')
|
|
58
|
+
.click();
|
|
59
|
+
await page.getByRole("button", { name: "Continue" }).click();
|
|
60
|
+
},
|
|
61
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
const process = (globalThis as any).process;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @tags @21cfr
|
|
6
|
+
*
|
|
7
|
+
* 21 CFR Part 11 — Test #6
|
|
8
|
+
* Creates a new child dataverse collection then deletes it.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
test(
|
|
12
|
+
"21 CFR: Create dataverse collection",
|
|
13
|
+
{ tag: ["@21cfr"] },
|
|
14
|
+
async ({ page }) => {
|
|
15
|
+
await page.goto(process.env.ROOT_DATAVERSE ?? "/");
|
|
16
|
+
await page.getByRole("button", { name: "Add Data" }).click();
|
|
17
|
+
await page.getByRole("link", { name: "New Dataverse" }).click();
|
|
18
|
+
await page
|
|
19
|
+
.getByRole("textbox", { name: "Identifier" })
|
|
20
|
+
.fill("playwright-testing-collection");
|
|
21
|
+
await page.getByLabel("Category").selectOption("Department");
|
|
22
|
+
await page.getByRole("button", { name: "Create Dataverse" }).click();
|
|
23
|
+
await page.getByRole("button", { name: "Edit" }).click();
|
|
24
|
+
await page.getByRole("link", { name: "Delete Dataverse" }).click();
|
|
25
|
+
await page.getByRole("button", { name: "Continue" }).click();
|
|
26
|
+
},
|
|
27
|
+
);
|