test: fix IDAA recovery meetings test — real backend save works
- Fix site_domain mock to return array { data: [mock_site_domain] } so
ae_api.headers['x-account-id'] gets a valid 11-char account_id.
Previously returned { data: {} } causing layout to fall back to 'ghost'
(5 chars) and the real API PATCH rejected the request with 422.
- Add integration test describe block (Real Backend Save) with
pass_through_event_patch option to let PATCH reach the real API.
- Extract localStorage injection into setup_idaa_auth() helper.
- Fix test name: 'sends PUT' → 'sends PATCH' (the API uses PATCH).
- All 14 tests pass.
This commit is contained in:
@@ -14,14 +14,14 @@
|
|||||||
|
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
import { ae_app_local_data_defaults } from './_helpers/ae_defaults';
|
import { ae_app_local_data_defaults } from './_helpers/ae_defaults';
|
||||||
import { testing_account_id } from './_helpers/env';
|
import { testing_account_id, mock_site_domain } from './_helpers/env';
|
||||||
|
|
||||||
// --- Test fixtures -----------------------------------------------------------
|
// --- Test fixtures -----------------------------------------------------------
|
||||||
|
|
||||||
const TEST_EVENT_ID = 'IDAA_RM_TEST01';
|
const TEST_EVENT_ID = '1Pkd025vvxU';// Per README test data
|
||||||
const TEST_NOVI_UUID = 'c9ea07b5-06b0-4a43-a2d0-8d06558c8a82'; // in default novi_trusted_li
|
const TEST_NOVI_UUID = 'c9ea07b5-06b0-4a43-a2d0-8d06558c8a82'; // in default novi_trusted_li
|
||||||
const TEST_NOVI_NAME = 'IDAA Test Member';
|
const TEST_NOVI_NAME = 'IDAA Test Novi Member';
|
||||||
const TEST_NOVI_EMAIL = 'testmember@idaa.org';
|
const TEST_NOVI_EMAIL = 'test+novi-member@oneskyit.com';
|
||||||
|
|
||||||
/** Minimal mock event object returned by the API. */
|
/** Minimal mock event object returned by the API. */
|
||||||
const mock_event = {
|
const mock_event = {
|
||||||
@@ -96,11 +96,58 @@ function build_idaa_loc_defaults(opts: { edit_event_obj?: boolean } = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject ae_loc + ae_idaa_loc into localStorage so the IDAA layout
|
||||||
|
* authenticates as a trusted member with the edit form open.
|
||||||
|
* Must be called via page.addInitScript BEFORE any navigation.
|
||||||
|
*/
|
||||||
|
async function setup_idaa_auth(page: any) {
|
||||||
|
await page.addInitScript(
|
||||||
|
({ ae_defaults, account_id, idaa_loc_defaults }: any) => {
|
||||||
|
const ae_loc_data = {
|
||||||
|
...ae_defaults,
|
||||||
|
account_id,
|
||||||
|
authenticated_access: true,
|
||||||
|
trusted_access: true,
|
||||||
|
administrator_access: false,
|
||||||
|
access_type: 'trusted',
|
||||||
|
iframe: false,
|
||||||
|
site_cfg_json: {
|
||||||
|
slct__event_id: null,
|
||||||
|
novi_admin_li: ['2b078deb-b4e7-4203-99da-9f7cd62159a5'],
|
||||||
|
novi_trusted_li: [
|
||||||
|
'c9ea07b5-06b0-4a43-a2d0-8d06558c8a82',
|
||||||
|
'58db22ee-4b0a-49a7-9f34-53d2ba85a84b'
|
||||||
|
],
|
||||||
|
admin_email: 'admin@oneskyit.com',
|
||||||
|
noreply_email: 'noreply@oneskyit.com'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.localStorage.setItem('ae_loc', JSON.stringify(ae_loc_data));
|
||||||
|
window.localStorage.setItem('ae_idaa_loc', JSON.stringify(idaa_loc_defaults));
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ae_defaults: ae_app_local_data_defaults,
|
||||||
|
account_id: testing_account_id,
|
||||||
|
idaa_loc_defaults: build_idaa_loc_defaults({ edit_event_obj: true })
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register route mocks for the V3 CRUD API and common lookup tables.
|
* Register route mocks for the V3 CRUD API and common lookup tables.
|
||||||
* Called inside each test after page creation.
|
* Called inside each test after page creation.
|
||||||
|
*
|
||||||
|
* @param pass_through_event_patch - When true, the event PATCH (save) is NOT
|
||||||
|
* intercepted and will reach the real backend. The GET is still mocked so
|
||||||
|
* the form loads immediately from seeded IDB. Use this for integration
|
||||||
|
* tests that need to verify actual persistence.
|
||||||
*/
|
*/
|
||||||
async function setup_api_mocks(page: any, event_id: string) {
|
async function setup_api_mocks(
|
||||||
|
page: any,
|
||||||
|
event_id: string,
|
||||||
|
opts: { pass_through_event_patch?: boolean } = {}
|
||||||
|
) {
|
||||||
await page.route('**/v3/**', async (route: any) => {
|
await page.route('**/v3/**', async (route: any) => {
|
||||||
const url = route.request().url();
|
const url = route.request().url();
|
||||||
const method = route.request().method();
|
const method = route.request().method();
|
||||||
@@ -116,6 +163,7 @@ async function setup_api_mocks(page: any, event_id: string) {
|
|||||||
|
|
||||||
// Event PATCH (update)
|
// Event PATCH (update)
|
||||||
if (url.includes(`/v3/crud/event/${event_id}`) && method === 'PATCH') {
|
if (url.includes(`/v3/crud/event/${event_id}`) && method === 'PATCH') {
|
||||||
|
if (opts.pass_through_event_patch) return route.continue();
|
||||||
return route.fulfill({
|
return route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
@@ -198,12 +246,14 @@ async function setup_api_mocks(page: any, event_id: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout/site domain lookup
|
// Layout/site domain lookup — must return a proper account_id so the
|
||||||
|
// layout builds ae_api.headers['x-account-id'] correctly.
|
||||||
|
// search_ae_obj_v3 expects { data: [array] }, not a single object.
|
||||||
if (url.includes('/v3/crud/site_domain') || url.includes('/v3/lookup/')) {
|
if (url.includes('/v3/crud/site_domain') || url.includes('/v3/lookup/')) {
|
||||||
return route.fulfill({
|
return route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
body: JSON.stringify({ data: {} })
|
body: JSON.stringify({ data: [mock_site_domain] })
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,42 +305,7 @@ test.describe('IDAA Recovery Meetings — Edit Form', () => {
|
|||||||
page.on('pageerror', (err: Error) =>
|
page.on('pageerror', (err: Error) =>
|
||||||
console.error(`BROWSER ERROR: ${err.message}`)
|
console.error(`BROWSER ERROR: ${err.message}`)
|
||||||
);
|
);
|
||||||
|
await setup_idaa_auth(page);
|
||||||
// Inject localStorage for both ae_loc and ae_idaa_loc
|
|
||||||
await page.addInitScript(
|
|
||||||
({ ae_defaults, account_id, idaa_loc_defaults }: any) => {
|
|
||||||
// ae_loc: full authenticated+trusted access
|
|
||||||
const ae_loc_data = {
|
|
||||||
...ae_defaults,
|
|
||||||
account_id,
|
|
||||||
authenticated_access: true,
|
|
||||||
trusted_access: true,
|
|
||||||
administrator_access: false,
|
|
||||||
access_type: 'trusted',
|
|
||||||
iframe: false,
|
|
||||||
site_cfg_json: {
|
|
||||||
slct__event_id: null,
|
|
||||||
novi_admin_li: ['2b078deb-b4e7-4203-99da-9f7cd62159a5'],
|
|
||||||
novi_trusted_li: [
|
|
||||||
'c9ea07b5-06b0-4a43-a2d0-8d06558c8a82',
|
|
||||||
'58db22ee-4b0a-49a7-9f34-53d2ba85a84b'
|
|
||||||
],
|
|
||||||
admin_email: 'admin@idaa.org',
|
|
||||||
noreply_email: 'noreply@idaa.org'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.localStorage.setItem('ae_loc', JSON.stringify(ae_loc_data));
|
|
||||||
|
|
||||||
// ae_idaa_loc: IDAA-specific state
|
|
||||||
window.localStorage.setItem('ae_idaa_loc', JSON.stringify(idaa_loc_defaults));
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ae_defaults: ae_app_local_data_defaults,
|
|
||||||
account_id: testing_account_id,
|
|
||||||
idaa_loc_defaults: build_idaa_loc_defaults({ edit_event_obj: true })
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await setup_api_mocks(page, TEST_EVENT_ID);
|
await setup_api_mocks(page, TEST_EVENT_ID);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -457,7 +472,7 @@ test.describe('IDAA Recovery Meetings — Edit Form', () => {
|
|||||||
await expect(save_btn).toBeEnabled();
|
await expect(save_btn).toBeEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('form submission sends PUT request to event API', async ({ page }) => {
|
test('form submission sends PATCH request to event API', async ({ page }) => {
|
||||||
await page.goto(`/`);
|
await page.goto(`/`);
|
||||||
await seed_event_idb(page, mock_event);
|
await seed_event_idb(page, mock_event);
|
||||||
await page.goto(`/idaa/recovery_meetings/${TEST_EVENT_ID}`);
|
await page.goto(`/idaa/recovery_meetings/${TEST_EVENT_ID}`);
|
||||||
@@ -491,6 +506,7 @@ test.describe('IDAA Recovery Meetings — Edit Form', () => {
|
|||||||
// Section 4: Virtual / Zoom platform section
|
// Section 4: Virtual / Zoom platform section
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
test('virtual checkbox reveals virtual/platform section', async ({ page }) => {
|
test('virtual checkbox reveals virtual/platform section', async ({ page }) => {
|
||||||
await page.goto(`/`);
|
await page.goto(`/`);
|
||||||
// Use a virtual-only event
|
// Use a virtual-only event
|
||||||
@@ -512,3 +528,61 @@ test.describe('IDAA Recovery Meetings — Edit Form', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Integration tests — real backend
|
||||||
|
// These tests use the real event ID and let CRUD calls reach the actual API.
|
||||||
|
// They will modify real data in the database.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
test.describe('IDAA Recovery Meetings — Real Backend Save (Integration)', () => {
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
page.on('pageerror', (err: Error) =>
|
||||||
|
console.error(`BROWSER ERROR: ${err.message}`)
|
||||||
|
);
|
||||||
|
await setup_idaa_auth(page);
|
||||||
|
// pass_through_event_patch=true: only PATCH for this event reaches the real API.
|
||||||
|
// Lookups and GET are still mocked so the form loads instantly from seeded IDB.
|
||||||
|
await setup_api_mocks(page, TEST_EVENT_ID, { pass_through_event_patch: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('save updated meeting name persists to backend', async ({ page }) => {
|
||||||
|
// Seed IDB so the form populates immediately without waiting for the real API GET.
|
||||||
|
// The mock_event name will appear in the field; we overwrite it before saving.
|
||||||
|
await page.goto(`/`);
|
||||||
|
await seed_event_idb(page, mock_event);
|
||||||
|
await page.goto(`/idaa/recovery_meetings/${TEST_EVENT_ID}`);
|
||||||
|
|
||||||
|
// Wait for the form field to be populated from IDB via liveQuery
|
||||||
|
const name_input = page.locator('input[name="name"]');
|
||||||
|
await expect(name_input).toBeVisible({ timeout: 15000 });
|
||||||
|
await expect(name_input).not.toHaveValue('', { timeout: 8000 });
|
||||||
|
|
||||||
|
// Read the current name and append a test marker (idempotent: strip any previous marker first)
|
||||||
|
const current_name = await name_input.inputValue();
|
||||||
|
const base_name = current_name.replace(/ \[PW\]$/, '').trim();
|
||||||
|
const new_name = `${base_name} [PW]`;
|
||||||
|
|
||||||
|
await name_input.fill(new_name);
|
||||||
|
|
||||||
|
const save_btn = page.locator('button[type="submit"]').filter({ hasText: /save/i }).first();
|
||||||
|
await expect(save_btn).toBeEnabled();
|
||||||
|
|
||||||
|
// Click Save and wait for the real PATCH response
|
||||||
|
const [response] = await Promise.all([
|
||||||
|
page.waitForResponse(
|
||||||
|
(resp) =>
|
||||||
|
resp.url().includes(`/v3/crud/event/${TEST_EVENT_ID}`) &&
|
||||||
|
resp.request().method() === 'PATCH',
|
||||||
|
{ timeout: 15000 }
|
||||||
|
),
|
||||||
|
save_btn.click()
|
||||||
|
]);
|
||||||
|
|
||||||
|
const body = await response.json();
|
||||||
|
expect(response.status(), 'PATCH should return HTTP 200').toBe(200);
|
||||||
|
expect(body?.data?.name ?? body?.data?.event_id, 'Response should include event data').toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user