Files
Gnuboard7/templates/_bundled/sirsoft-basic/tests/Playwright/playwright.config.ts
T
HeuJung 6b3903553a fix(e2e,a11y): 확장 E2E 로케일 고정 + 관리자 탭·스위치 접근성 보완
확장 E2E config 의 로케일 미고정을 코어 포함 7개에서 해소했다. 미지정 시 첫 페이지
로드의 Accept-Language 가 en-US 로 나가고 엔진이 그 값을 localStorage 에 굳혀,
인증 후에도 세션 전체가 영어로 렌더돼 한국어 문구를 단언하는 spec 이 전멸한다.
같은 함정을 정적으로 막는 룰을 함께 등록했다.

관리자 공용 탭·스위치는 ARIA 와 키보드 처리가 없어 마우스 없이는 조작할 수 없었고
보조기기에 목록·선택 상태가 전달되지 않았다. WAI-ARIA 규약대로 보완하고, role 변경에
맞춰 낡은 셀렉터를 갱신했다(admin_basic E2E 회귀 1건 포함).

nhnkcp 설정 화면은 저장 후 정규화 결과가 입력칸에 반영되지 않았다. 계측 결과
refetch 와 initLocal 은 정상 동작하며 _local 도 갱신되지만 사용자가 편집한 입력칸만
값을 유지한다. 저장 응답으로 폼을 명시 재바인딩하고 그 기전을 트러블슈팅에 남겼다.

safelist 생성기가 빌드마다 main.css 에 빈 줄을 누적해 매 빌드가 소스를 변경된 것으로
만들던 문제도 함께 고쳤다.
2026-07-31 00:43:56 +09:00

102 lines
4.2 KiB
TypeScript

/**
* sirsoft-basic 템플릿 Playwright E2E 설정.
*
* 코어 `playwright.config.ts` 와 동일한 base URL 해석 우선순위를 따른다 — 활성 호스트가
* 가변(개발자/CI/운영 환경별로 다른 도메인)이므로 하드코딩 회피.
*
* Base URL 해석:
* 1. PLAYWRIGHT_BASE_URL 환경변수 (CI/명시적 오버라이드)
* 2. .env (코어 루트) 의 APP_URL — 단 localhost 류는 fallback 부적합
* 3. 그 외 — 명시 에러
*
* 뷰포트는 각 spec 이 `page.setViewportSize()` 로 직접 지정한다(모바일 390/320px ↔ 데스크톱
* 1280px 을 한 파일에서 교차 검증하므로, 프로젝트를 뷰포트별로 나누면 같은 테스트가
* 중복 실행된다).
*
* 실행 예시:
* PowerShell — $env:PLAYWRIGHT_BASE_URL='https://example.com'; npx playwright test --config templates/_bundled/sirsoft-basic/tests/Playwright/playwright.config.ts
*/
import { defineConfig, devices } from '@playwright/test';
import { readFileSync, existsSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
/**
* 코어 루트 (artisan / .env / Playwright 산출물의 기준 경로).
*
* 확장 config 는 확장 디렉토리에서 실행되지만, 산출물을 그 안에 쓰면 Windows 에서
* `{module|template}:update` 의 디렉토리 이동이 열린 핸들에 걸려 실패한다.
* 산출물은 코어 루트 아래로 모아 update 경로와 분리한다 (.gitignore 가 이미 덮는 위치).
*/
const CORE_ROOT = process.env.G7_ROOT || resolve(__dirname, '../../../../../');
/** 확장별 산출물 격리 — 확장끼리 리포트를 덮어쓰지 않도록 slug 로 네임스페이스. */
const ARTIFACT_SLUG = 'templates/sirsoft-basic';
/**
* .env 파일에서 키 하나를 읽습니다.
*
* @param filePath .env 파일 절대 경로
* @param key 읽을 키 이름
* @return 값 (없으면 null)
*/
function readEnvFile(filePath: string, key: string): string | null {
if (!existsSync(filePath)) return null;
const content = readFileSync(filePath, { encoding: 'utf-8' });
const pattern = new RegExp(`^${key}=(.*)$`, 'm');
const match = content.match(pattern);
if (!match) return null;
let value = match[1].trim();
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
value = value.slice(1, -1);
}
return value || null;
}
/**
* E2E base URL 을 해석합니다.
*
* @return base URL
* @throws Error 활성 호스트를 확정할 수 없을 때
*/
function resolveBaseUrl(): string {
if (process.env.PLAYWRIGHT_BASE_URL) {
return process.env.PLAYWRIGHT_BASE_URL;
}
const appUrl = readEnvFile(resolve(CORE_ROOT, '.env'), 'APP_URL');
if (appUrl && !/^https?:\/\/localhost(:\d+)?\/?$/i.test(appUrl)) {
return appUrl;
}
throw new Error(
'sirsoft-basic 템플릿 E2E base URL 미설정. PLAYWRIGHT_BASE_URL 환경변수를 지정하거나 코어 .env 의 APP_URL 을 활성 호스트로 설정하세요.'
);
}
export default defineConfig({
testDir: './specs',
outputDir: resolve(CORE_ROOT, 'test-results', ARTIFACT_SLUG),
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html', { outputFolder: resolve(CORE_ROOT, 'playwright-report', ARTIFACT_SLUG), open: 'never' }],
['list'],
],
use: {
baseURL: resolveBaseUrl(),
// spec 이 한국어 화면 문구를 단언하므로 로케일을 고정한다.
// 로케일 우선순위는 localStorage g7_locale → 서버 응답값 → 'ko' 이고, 서버값은 미인증
// 요청에서 Accept-Language 로 결정된다(SetLocale 미들웨어). Playwright 의 locale 옵션이
// 그 헤더를 만들므로, 지정하지 않으면 첫 페이지 로드가 en-US 로 나가 화면이 영어로 렌더되고
// 엔진이 그 값을 localStorage 에 저장해 이후 인증해도 세션 전체가 영어로 고정된다.
locale: 'ko-KR',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
ignoreHTTPSErrors: true,
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
});