Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/database/foxx/api/user_router.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ router
result = [user.new];
},
});
res.send(result);
logger.logRequestSuccess({
client: client?._id,
correlationId: req.headers["x-correlation-id"],
Expand Down
73 changes: 73 additions & 0 deletions tests/end-to-end/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,76 @@ To use the python API you will need to build it
cmake -S. -B build -DBUILD_PYTHON_CLIENT=ON
cmake --build build --target pydatafed
```

## Playwright

On windows, it is recommended to run playwright directly on windows and not in
a docker container or on wsl2. If you do take that approach you will likely
encounter compatibility problems, and will still need to stand up an XServer
on the windows host.

To run

```bash
npm install .
npx playwright install
npx playwright test
```

You can also use the playwright code generator to add additional tests.

```bash
npx playwright codegen
```

If you are running on linux you might be able to get away with running in a
docker image.

Below is a minimal dockerfile to build playwright with a few useful developer tools.

```Dockerfile
FROM mcr.microsoft.com/playwright:v1.45.1-noble

# Install Chromium only
WORKDIR /work
RUN npx playwright install chromium --with-deps; npx playwright install
RUN apt-get update && apt-get install -y ca-certificates bash vim && update-ca-certificates
```

Build it with.

```bash
docker build . -t playwright:latest
```

```bash
docker run --rm -v "$PWD:/work" -w /work -e DATAFED_WEB_TEST_USERNAME="$DATAFED_WEB_TEST_USERNAME" -e DATAFED_WEB_TEST_PASSWORD="$DATAFED_WEB_TEST_PASSWORD" -e DATAFED_DOMAIN="$DATAFED_DOMAIN" -e DISPLAY=host.docker.
internal:0 playwright:latest npx -y playwright test
```

NOTE: By default the web tests are setup to run in headless mode but if you
wish to see the web tests as they execute while debugging etc you will need
to edit the configuration in playwright.config.js

This might need to be specified in the following places
```
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
headless: false, // optional: run headed
},
},
]
```

```
use: {
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
headless: false,
screenshot: "only-on-failure",
```

2 changes: 1 addition & 1 deletion tests/end-to-end/web-UI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/auth.setup.js"
@ONLY
)

message("ENABLE END TO END!!!!!!!")
#FIXTHIS
# For E2E web ui test
if(ENABLE_END_TO_END_WEB_TESTS)
Expand Down
79 changes: 56 additions & 23 deletions tests/end-to-end/web-UI/auth.setup.js.in
Original file line number Diff line number Diff line change
@@ -1,69 +1,102 @@
const { chromium } = require('playwright');
const path = require('path');
const process = require('process');
const { chromium } = require("playwright");
const path = require("path");
const process = require("process");

console.log("INFO - ******Inside Setup file******");

module.exports = async function () {
// if a playwright page object doesn't exist, create one
const browser = await chromium.launch({
args: ['--ignore-certificate-errors'],
args: ["--ignore-certificate-errors"],
headless: true,
});

const page = await browser.newPage();
const page = await browser.newPage();
console.log("INFO - new page object created");

// Extra safety for debugging slow loads
page.on('console', msg => console.log('INFO - [PAGE LOG] ', msg.text()));
page.on('response', res => {
if (res.status() >= 400)
console.log(`ERROR - [HTTP ${res.status()}] ${res.url()}`);
page.on("console", (msg) => console.log("INFO - [PAGE LOG] ", msg.text()));
page.on("response", (res) => {
if (res.status() >= 400) console.log(`ERROR - [HTTP ${res.status()}] ${res.url()}`);
});

// --- Step 1: Go to DataFed ---
console.log("INFO - 1. Got to DataFed");
await page.goto('https://@DATAFED_DOMAIN@/ui/welcome', { waitUntil: 'networkidle', timeout: 60000 });
await page.goto("https://@DATAFED_DOMAIN@/ui/welcome", {
waitUntil: "networkidle",
timeout: 60000,
});

// --- Step 2: Click Login/Register ---
console.log("INFO - 2. Login and Register");
const loginButton = page.getByRole('button', { name: 'Log In / Register' });
await loginButton.waitFor({ state: 'visible', timeout: 30000 });
const loginButton = page.getByRole("button", { name: "Log In / Register" });
await loginButton.waitFor({ state: "visible", timeout: 30000 });
await loginButton.click();

// --- Step 3: Check if link is visible ---
console.log("INFO - 3. Check if link is visible");
const globusLink = page.getByRole('link', { name: /Globus/i });
await globusLink.waitFor({ state: 'visible', timeout: 30000 });
const globusLink = page.getByRole("link", { name: /Globus/i });
await globusLink.waitFor({ state: "visible", timeout: 30000 });
await page.waitForTimeout(1000);

// --- Step 4: Click Globus ID to sign in ---
console.log("INFO - 4. Use Globus ID to sign in");
const globusIDButton = page.getByRole('button', { name: 'Globus ID to sign in' });
await globusIDButton.waitFor({ state: 'visible', timeout: 30000 });
const globusIDButton = page.getByRole("button", { name: "Globus ID to sign in" });
await globusIDButton.waitFor({ state: "visible", timeout: 30000 });
await globusIDButton.click();
await page.waitForTimeout(1000);

// --- Step 5: Wait for Globus redirect ---
console.log("INFO - 5. Wait for Globus login to redirect");
await page.waitForLoadState('networkidle', { timeout: 45000 });
await page.waitForLoadState("networkidle", { timeout: 45000 });

// --- Step 6: Fill in credentials robustly ---
console.log("INFO - 6. Fill in credentials");
const usernameField = page.getByLabel(/username/i);
await usernameField.waitFor({ state: 'visible', timeout: 45000 });
await usernameField.waitFor({ state: "visible", timeout: 45000 });
await usernameField.fill(process.env.DATAFED_WEB_TEST_USERNAME);

const passwordField = page.getByLabel(/password/i);
await passwordField.waitFor({ state: 'visible', timeout: 45000 });
await passwordField.waitFor({ state: "visible", timeout: 45000 });
await passwordField.fill(process.env.DATAFED_WEB_TEST_PASSWORD);

// --- Step 7: Submit form ---
await page.click('button[type="submit"]');
await page.waitForURL('https://@DATAFED_DOMAIN@/ui/main')

await Promise.race([
page.waitForURL(/\/ui\/main$/, { timeout: 45000 }),
page.waitForURL(/\/ui\/register$/, { timeout: 45000 }),
]);

let current = page.url();
console.log("INFO - 7a Redirected to:", current);

if (current.includes("/ui/register")) {
console.log("INFO - 7b. Registering:", current);
await page.getByRole('button', { name: 'Continue Registration' }).click();
} else {
console.log("INFO - 7b. Grabbing fresh url:", current);
if (!page.url().includes("/ui/main")) {
await page.waitForURL("https://@DATAFED_DOMAIN@/ui/main", { timeout: 45000 });
}
current = page.url();
console.log("INFO - 7c. URL is: ", current);
}

await page.screenshot({ path: "after_login.png", fullPage: true });
if (current.includes("/ui/main")) {
console.log("INFO - 8b Successful: ", current);
} else {
throw new Error(`Unexpected redirect URL: ${current}`);
}
//await Promise.all([
// page.waitForNavigation({ url: /\/ui\/main/ }), // robust matching
// page.click('button[type="submit"]'),
//]);
//page.screenshot(path="final.png", full_page=True)
console.log("INFO - ******PAST LOGIN******");
await page.context().storageState({ path: './.auth/auth.json'}); //TESTING
await page.context().storageState({ path: "./.auth/auth.json" }); //TESTING
console.log("INFO - ******Done with login******");

await browser.close();
};

14 changes: 0 additions & 14 deletions tests/end-to-end/web-UI/playwright.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,5 @@ module.exports = defineConfig({
...devices["Desktop Chrome"],
},
},

// {
// name: 'firefox',
// use: {
// ...devices['Desktop Firefox'],
// },
// },

// {
// name: 'webkit',
// use: {
// ...devices['Desktop Safari'],
// },
// },
],
});
80 changes: 36 additions & 44 deletions tests/end-to-end/web-UI/scripts/testingBasicFunction.spec.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,46 @@
import { test, expect } from "@playwright/test";

// checking visibility and expanding some dropdowns
test("test visibility", async ({ page }) => {
try {
console.log("******Begin test******");
// Temporary fix
let domain = process.env.DATAFED_DOMAIN;
await page.goto("https://" + domain + "/");
if (await page.getByRole("button", { name: "Log In / Register" }).isVisible()) {
console.log("NOT LOGGED IN");
test.describe("DataFed UI Navigation", () => {
test("should display main navigation elements", async ({ page }) => {
const domain = process.env.DATAFED_DOMAIN;
if (!domain) {
throw new Error("DATAFED_DOMAIN environment variable not set");
}
if (await expect(page.getByText("Continue Registration")).toBeVisible()) {
await page.getByText("Continue Registration").click({ timeout: 20000 });
}
await expect(page.locator(".ui-icon").first()).toBeVisible({
timeout: 20000,
});

await page.goto(`https://${domain}/ui/main`);

// Verify main elements
await expect(page.locator(".ui-icon").first()).toBeVisible();
await expect(page.getByText("DataFed - Scientific Data")).toBeVisible();
await expect(page.getByRole("link", { name: "My Data" })).toBeVisible();
await expect(page.getByRole("link", { name: "Catalog" })).toBeVisible();
await expect(page.getByRole("button", { name: "" })).toBeVisible();
});

test("should expand tree navigation items", async ({ page }) => {
const domain = process.env.DATAFED_DOMAIN;
if (!domain) {
throw new Error("DATAFED_DOMAIN environment variable not set");
}

await page.goto(`https://${domain}/ui/main`);

// Define tree items to expand
const treeItems = [
"Public Collections",
"Allocations",
"Project Data",
"Shared Data",
"Saved Queries",
"By User",
];

await page
.getByRole("treeitem", { name: "  Public Collections" })
.getByRole("button")
.click();
await page
.getByRole("treeitem", { name: "  Public Collections" })
.getByRole("group")
.click();
await page.getByRole("treeitem", { name: "  Allocations" }).getByRole("button").click();
await page.getByRole("treeitem", { name: "  Project Data" }).getByRole("button").click();
await page.getByRole("treeitem", { name: "  Shared Data" }).getByRole("button").click();
await page
.getByRole("treeitem", { name: "  Saved Queries" })
.locator("span")
.first()
.click();
await page.getByRole("treeitem", { name: "  Saved Queries" }).getByRole("button").click();
await page.getByText("Provenance Annotate Upload").click({ timeout: 20000 });
await page.getByRole("treeitem", { name: "  By User" }).getByRole("button").click();
} catch (error) {
// element not visible, either the test broke due to tags changing, or not logged in
// try to log out, because if not logged out, future tests will fail due to globus being annoying
if (await page.getByRole("button", { name: "" }).isVisible()) {
await page.getByRole("button", { name: "" }).click();
} else {
// if in here, check if you logged out properly
throw error;
for (const item of treeItems) {
const treeItem = page.getByRole("treeitem", { name: new RegExp(item) });
const button = treeItem.getByRole("button").first();
await expect(button).toBeVisible();
await button.click();
// Add assertion that it expanded if needed
}
}
//removed logout
});
});
Loading