112 lines
4.9 KiB
JavaScript
112 lines
4.9 KiB
JavaScript
|
||
import { test, expect } from '@playwright/test';
|
||
import { loadTestUser } from '../utils/testUser.js';
|
||
|
||
test.describe('@p1 Career Explorer — CareerSearch datalist', () => {
|
||
test.setTimeout(20000);
|
||
|
||
test('datalist commit opens modal; Change resets input', async ({ page }) => {
|
||
const user = loadTestUser();
|
||
|
||
// Helpers
|
||
async function closeAnyOverlay() {
|
||
const overlay = page.locator('div.fixed.inset-0');
|
||
if (!(await overlay.isVisible({ timeout: 500 }).catch(() => false))) return;
|
||
|
||
const dlg = overlay.locator('div[role="dialog"], div.bg-white').first();
|
||
|
||
// If modal asks for ratings, pick neutral values and Save/Continue so it goes away.
|
||
const selects = dlg.locator('select');
|
||
const sc = await selects.count().catch(() => 0);
|
||
for (let i = 0; i < sc; i++) {
|
||
const sel = selects.nth(i);
|
||
if (await sel.isVisible().catch(() => false)) {
|
||
const has3 = await sel.locator('option[value="3"]').count().catch(() => 0);
|
||
if (has3) await sel.selectOption('3');
|
||
else {
|
||
const opts = sel.locator('option');
|
||
const n = await opts.count().catch(() => 0);
|
||
for (let j = 0; j < n; j++) {
|
||
const v = await opts.nth(j).getAttribute('value');
|
||
if (v) { await sel.selectOption(v); break; }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const tb = dlg.locator('input, textarea, [role="textbox"]').first();
|
||
if (await tb.isVisible().catch(() => false)) await tb.fill('3');
|
||
|
||
const save = dlg.getByRole('button', { name: /(Save|Continue|Done|OK)/i });
|
||
const cancel = dlg.getByRole('button', { name: /(Cancel|Close)/i });
|
||
if (await save.isVisible({ timeout: 500 }).catch(() => false)) await save.click();
|
||
else if (await cancel.isVisible({ timeout: 500 }).catch(() => false)) await cancel.click();
|
||
else await page.keyboard.press('Escape');
|
||
|
||
await overlay.waitFor({ state: 'hidden', timeout: 5000 }).catch(() => {});
|
||
}
|
||
|
||
// Sign in
|
||
await page.context().clearCookies();
|
||
await page.goto('/signin', { waitUntil: 'networkidle' });
|
||
await page.getByPlaceholder('Username', { exact: true }).fill(user.username);
|
||
await page.getByPlaceholder('Password', { exact: true }).fill(user.password);
|
||
await page.getByRole('button', { name: /^Sign In$/ }).click();
|
||
await page.waitForURL('**/signin-landing**', { timeout: 15000 });
|
||
|
||
// Go to Career Explorer
|
||
await page.goto('/career-explorer', { waitUntil: 'networkidle' });
|
||
await expect(page.getByRole('heading', { name: /Explore Careers - use these tools/i })).toBeVisible();
|
||
await closeAnyOverlay();
|
||
|
||
const input = page.getByPlaceholder('Start typing a career...');
|
||
// If previously selected, the input is disabled; click Change to reset.
|
||
if (await input.isDisabled()) {
|
||
const changeLink = page.getByRole('button', { name: /^Change$/i }).or(page.getByText(/^Change$/));
|
||
await expect(changeLink).toBeVisible({ timeout: 5000 });
|
||
await changeLink.click();
|
||
await expect(input).toBeEnabled({ timeout: 2000 });
|
||
await expect(input).toHaveValue('');
|
||
}
|
||
|
||
// Type a partial and wait for datalist options to populate
|
||
await input.fill('block');
|
||
const options = page.locator('datalist#career-titles option');
|
||
await expect
|
||
.poll(async () => await options.count(), { timeout: 7000, message: 'no datalist options' })
|
||
.toBeGreaterThan(0);
|
||
|
||
// Take the first suggestion's exact value (e.g., "Blockchain Engineers")
|
||
const firstValue = await options.first().evaluate(el => el.value);
|
||
|
||
// Commit by setting exact value + blur (component commits on exact + blur)
|
||
await input.fill(firstValue);
|
||
await input.blur();
|
||
|
||
// Loading overlay may show; wait for it to appear (optional) then hide
|
||
const loading = page.getByText(/Loading Career/i).first(); // matches “Loading Career Suggestions…”
|
||
await loading.isVisible({ timeout: 2000 }).catch(() => {});
|
||
await loading.waitFor({ state: 'hidden', timeout: 60000 }).catch(() => {}); // guard slow cold path
|
||
|
||
// CareerModal should open (Add to Comparison button present)
|
||
await expect(page.getByRole('button', { name: /Add to Comparison/i })).toBeVisible({ timeout: 15000 });
|
||
|
||
// Close the modal (don’t add in this test)
|
||
const closeBtn = page.getByRole('button', { name: /^Close$/i });
|
||
if (await closeBtn.isVisible().catch(() => false)) {
|
||
await closeBtn.click();
|
||
} else {
|
||
await page.keyboard.press('Escape');
|
||
}
|
||
|
||
// Input becomes disabled after selection; click Change to reset
|
||
await expect(input).toBeDisabled();
|
||
const changeLink = page.getByRole('button', { name: /^Change$/i }).or(page.getByText(/^Change$/));
|
||
await expect(changeLink).toBeVisible({ timeout: 5000 });
|
||
await changeLink.click();
|
||
|
||
// Now the input should be enabled and cleared
|
||
await expect(input).toBeEnabled();
|
||
await expect(input).toHaveValue('');
|
||
});
|
||
});
|