141 lines
6.9 KiB
JavaScript
141 lines
6.9 KiB
JavaScript
import { chromium } from '@playwright/test';
|
|
import { createHash } from 'node:crypto';
|
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
import { resolve } from 'node:path';
|
|
|
|
const baseUrl = process.env.REF_GENERAL_URL ?? 'http://127.0.0.1:3416/sam/';
|
|
const username = process.env.REF_GENERAL_USER ?? 's100user01';
|
|
const passwordFile = process.env.REF_GENERAL_PASSWORD_FILE;
|
|
const artifactRoot = resolve(process.env.REF_GENERAL_ARTIFACT_DIR ?? 'test-results/reference-nation-general-controls');
|
|
|
|
if (!passwordFile) throw new Error('REF_GENERAL_PASSWORD_FILE is required.');
|
|
const password = (await readFile(passwordFile, 'utf8')).trim();
|
|
await mkdir(artifactRoot, { recursive: true });
|
|
|
|
const measure = async (page) =>
|
|
page.evaluate(() => {
|
|
const describe = (element) => {
|
|
if (!(element instanceof HTMLElement)) return null;
|
|
const rect = element.getBoundingClientRect();
|
|
const style = getComputedStyle(element);
|
|
return {
|
|
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
style: {
|
|
fontFamily: style.fontFamily,
|
|
fontSize: style.fontSize,
|
|
lineHeight: style.lineHeight,
|
|
color: style.color,
|
|
backgroundColor: style.backgroundColor,
|
|
borderCollapse: style.borderCollapse,
|
|
},
|
|
};
|
|
};
|
|
const headerGroups = [...document.querySelectorAll('.ag-header-group-cell')].map((element) => ({
|
|
text: element.textContent?.trim() ?? '',
|
|
className: element.className,
|
|
expanded: element.getAttribute('aria-expanded'),
|
|
rect: describe(element)?.rect,
|
|
}));
|
|
const columns = [...document.querySelectorAll('.ag-header-cell')].map((element) => ({
|
|
id: element.getAttribute('col-id'),
|
|
text: element.textContent?.trim() ?? '',
|
|
sort: element.getAttribute('aria-sort'),
|
|
}));
|
|
const inputs = [...document.querySelectorAll('.ag-header-row-column-filter input.ag-text-field-input')].map(
|
|
(element) => {
|
|
const rect = element.getBoundingClientRect();
|
|
const center = rect.x + rect.width / 2;
|
|
const matchingHeader = [...document.querySelectorAll('.ag-header-row-column .ag-header-cell')].find(
|
|
(candidate) => {
|
|
const headerRect = candidate.getBoundingClientRect();
|
|
return center >= headerRect.x && center <= headerRect.right;
|
|
}
|
|
);
|
|
return {
|
|
colId: matchingHeader?.getAttribute('col-id') ?? null,
|
|
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
placeholder: element.getAttribute('placeholder'),
|
|
};
|
|
}
|
|
);
|
|
return {
|
|
url: location.href,
|
|
documentWidth: document.documentElement.scrollWidth,
|
|
body: describe(document.body),
|
|
page: describe(document.querySelector('.pageNationGeneral')),
|
|
component: describe(document.querySelector('.component-general-list')),
|
|
grid: describe(document.querySelector('.ag-root-wrapper')),
|
|
firstRow: describe(document.querySelector('.ag-row')),
|
|
headerGroups,
|
|
columns,
|
|
inputs,
|
|
toolbarText: document.querySelector('.component-general-list')?.textContent?.slice(0, 500) ?? '',
|
|
};
|
|
});
|
|
|
|
const browser = await chromium.launch({ headless: true });
|
|
try {
|
|
const context = await browser.newContext({
|
|
viewport: { width: 1200, height: 900 },
|
|
deviceScaleFactor: 1,
|
|
locale: 'ko-KR',
|
|
colorScheme: 'dark',
|
|
});
|
|
const page = await context.newPage();
|
|
await page.goto(baseUrl, { waitUntil: 'networkidle' });
|
|
const salt = await page.locator('#global_salt').inputValue();
|
|
const passwordHash = createHash('sha512')
|
|
.update(salt + password + salt)
|
|
.digest('hex');
|
|
const login = await context.request.post(new URL('api.php?path=Login/LoginByID', baseUrl).toString(), {
|
|
data: { username, password: passwordHash },
|
|
});
|
|
const loginResult = await login.json();
|
|
if (!login.ok() || loginResult.result !== true) throw new Error('Reference login failed.');
|
|
|
|
await page.goto(new URL('hwe/', baseUrl).toString(), { waitUntil: 'domcontentloaded' });
|
|
await page.goto(new URL('hwe/v_nationGeneral.php', baseUrl).toString(), { waitUntil: 'domcontentloaded' });
|
|
await page.locator('.ag-root-wrapper').waitFor();
|
|
await page.locator('.ag-center-cols-container .ag-row').first().waitFor();
|
|
await page.evaluate(() => document.fonts.ready);
|
|
|
|
const output = { initial: await measure(page) };
|
|
await page.screenshot({ path: resolve(artifactRoot, 'ref-initial.png'), fullPage: true });
|
|
|
|
const statGroup = page.locator('.ag-header-group-cell').filter({ hasText: '능력치' });
|
|
await statGroup.locator('.ag-header-expand-icon:visible').click();
|
|
output.collapsed = await measure(page);
|
|
await page.screenshot({ path: resolve(artifactRoot, 'ref-stat-collapsed.png'), fullPage: true });
|
|
|
|
const leadershipHeader = page.locator('.ag-header-cell[col-id="leadership"] .ag-header-cell-label');
|
|
await statGroup.locator('.ag-header-expand-icon:visible').click();
|
|
await leadershipHeader.click();
|
|
output.sort = {
|
|
ariaSort: await page.locator('.ag-header-cell[col-id="leadership"]').getAttribute('aria-sort'),
|
|
firstValue: await page.locator('.ag-center-cols-container .ag-row [col-id="leadership"]').first().innerText(),
|
|
};
|
|
|
|
await statGroup.locator('.ag-header-expand-icon:visible').click();
|
|
await page.getByRole('button', { name: /보기 모드/ }).click();
|
|
page.once('dialog', (dialog) => dialog.accept('Ref 캡처'));
|
|
await page.getByText('보관하기', { exact: true }).click();
|
|
output.savedSetting = await page.evaluate(() => ({
|
|
settings: localStorage.getItem('GeneralListDisplaySetting'),
|
|
last: localStorage.getItem('LastUsedSettingsKey_pageNationGeneral'),
|
|
}));
|
|
await page.reload({ waitUntil: 'domcontentloaded' });
|
|
await page.locator('.ag-root-wrapper').waitFor();
|
|
await page.locator('.ag-center-cols-container .ag-row').first().waitFor();
|
|
output.reloaded = await measure(page);
|
|
|
|
await page.getByRole('button', { name: /보기 모드/ }).click();
|
|
page.once('dialog', (dialog) => dialog.accept());
|
|
await page.getByRole('button', { name: '삭제', exact: true }).click();
|
|
output.deletedSetting = await page.evaluate(() => localStorage.getItem('GeneralListDisplaySetting'));
|
|
|
|
await writeFile(resolve(artifactRoot, 'computed-dom.json'), `${JSON.stringify(output, null, 2)}\n`);
|
|
process.stdout.write(`${JSON.stringify({ ok: true, artifactRoot, output })}\n`);
|
|
} finally {
|
|
await browser.close();
|
|
}
|