Skip to content

Commit dd8ac56

Browse files
committed
test: e2e foundation (chromium限定化 + helper関数拡充)
後続フェーズの E2E テスト拡充で共通利用する基盤を整備。 - playwright.config.ts を chromium 専用にして iteration 速度を優先 (クロスブラウザ互換は maplibre 本体のテストに委ねる) - e2e/helper.ts に waitForStyleLoad / mockGeoloniaTiles / recordRequests / getCenter 等の共通ヘルパー 11 関数を追加 - e2e/sample.spec.ts で helper の sanity check (2 件) Refs #488, #411
1 parent 1423e5b commit dd8ac56

3 files changed

Lines changed: 187 additions & 35 deletions

File tree

e2e/helper.ts

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,155 @@
11
import { Page } from '@playwright/test';
2-
// テスト用の設定
2+
33
export const TEST_URL = 'http://localhost:3000/e2e';
44
export const LOAD_TIMEOUT = 5000;
55

6-
// ヘルパー関数
6+
/** 地図コンテナと canvas の出現を待つ。既存テスト互換。 */
77
export async function waitForMapLoad(page: Page, selector = '.geolonia') {
8-
// 地図コンテナの存在を確認
98
await page.waitForSelector(selector);
10-
// Maplibreのcanvasが表示されるまで待機
119
await page.waitForSelector(`${selector} canvas`, { timeout: LOAD_TIMEOUT });
1210
}
11+
12+
/** Maplibre Map の load + isStyleLoaded を待つ。スタイル依存テスト向け。 */
13+
export async function waitForStyleLoad(page: Page, selector = '#map') {
14+
await page.waitForFunction(
15+
(sel) => {
16+
const el = document.querySelector(sel) as any;
17+
return !!(el && el.geoloniaMap && el.geoloniaMap.loaded() && el.geoloniaMap.isStyleLoaded());
18+
},
19+
selector,
20+
{ timeout: LOAD_TIMEOUT },
21+
);
22+
}
23+
24+
/** container.geoloniaMap が生成済みか確認。 */
25+
export async function hasMapInstance(page: Page, selector = '#map'): Promise<boolean> {
26+
return page.evaluate((sel) => {
27+
const el = document.querySelector(sel) as any;
28+
return !!(el && el.geoloniaMap);
29+
}, selector);
30+
}
31+
32+
export async function getCenter(page: Page, selector = '#map'): Promise<{ lat: number; lng: number }> {
33+
return page.evaluate((sel) => {
34+
const el = document.querySelector(sel) as any;
35+
const c = el.geoloniaMap.getCenter();
36+
return { lat: c.lat, lng: c.lng };
37+
}, selector);
38+
}
39+
40+
export async function getZoom(page: Page, selector = '#map'): Promise<number> {
41+
return page.evaluate((sel) => {
42+
const el = document.querySelector(sel) as any;
43+
return el.geoloniaMap.getZoom();
44+
}, selector);
45+
}
46+
47+
export async function getBearing(page: Page, selector = '#map'): Promise<number> {
48+
return page.evaluate((sel) => {
49+
const el = document.querySelector(sel) as any;
50+
return el.geoloniaMap.getBearing();
51+
}, selector);
52+
}
53+
54+
export async function getPitch(page: Page, selector = '#map'): Promise<number> {
55+
return page.evaluate((sel) => {
56+
const el = document.querySelector(sel) as any;
57+
return el.geoloniaMap.getPitch();
58+
}, selector);
59+
}
60+
61+
export async function getStyleLayerIds(page: Page, selector = '#map'): Promise<string[]> {
62+
return page.evaluate((sel) => {
63+
const el = document.querySelector(sel) as any;
64+
return el.geoloniaMap.getStyle().layers.map((l: any) => l.id);
65+
}, selector);
66+
}
67+
68+
export async function hasLayer(page: Page, layerId: string, selector = '#map'): Promise<boolean> {
69+
return page.evaluate(
70+
({ sel, id }) => {
71+
const el = document.querySelector(sel) as any;
72+
return !!el.geoloniaMap.getLayer(id);
73+
},
74+
{ sel: selector, id: layerId },
75+
);
76+
}
77+
78+
export async function hasSource(page: Page, sourceId: string, selector = '#map'): Promise<boolean> {
79+
return page.evaluate(
80+
({ sel, id }) => {
81+
const el = document.querySelector(sel) as any;
82+
return !!el.geoloniaMap.getSource(id);
83+
},
84+
{ sel: selector, id: sourceId },
85+
);
86+
}
87+
88+
/**
89+
* Geolonia のスタイル/タイル/スプライト等を最小レスポンスでモックする。
90+
* テスト先頭の beforeEach で呼ぶ前提。
91+
*
92+
* 返すスタイルは layers=[background] のみのミニマル style v8。
93+
* 認証URL検証など transformRequest の挙動は実リクエスト前にここで握る。
94+
*/
95+
export async function mockGeoloniaTiles(page: Page) {
96+
// スタイル JSON (cdn.geolonia.com/style/...)
97+
await page.route(/cdn\.geolonia\.com\/style\/.+\.json/, (route) =>
98+
route.fulfill({
99+
status: 200,
100+
contentType: 'application/json',
101+
body: JSON.stringify({
102+
version: 8,
103+
sources: {},
104+
layers: [
105+
{ id: 'background', type: 'background', paint: { 'background-color': '#cccccc' } },
106+
],
107+
}),
108+
}),
109+
);
110+
111+
// スプライト / グリフ / フォント関連は 404 でよい (background のみなので参照されない)
112+
await page.route(/cdn\.geolonia\.com\/(sprite|glyph|font)/, (route) =>
113+
route.fulfill({ status: 404, body: '' }),
114+
);
115+
116+
// TileJSON
117+
await page.route(/tileserver\.geolonia\.com\/.*/, (route) =>
118+
route.fulfill({
119+
status: 200,
120+
contentType: 'application/json',
121+
body: JSON.stringify({
122+
tilejson: '2.2.0',
123+
tiles: ['https://tiles.geolonia.com/{z}/{x}/{y}.pbf'],
124+
minzoom: 0,
125+
maxzoom: 14,
126+
}),
127+
}),
128+
);
129+
130+
// ベクトルタイル
131+
await page.route(/\.tiles\.geolonia\.com\/.*\.pbf/, (route) =>
132+
route.fulfill({ status: 204, body: '' }),
133+
);
134+
135+
// api.geolonia.com (legacy / permission check 等)
136+
await page.route(/api\.geolonia\.com\/.*/, (route) =>
137+
route.fulfill({ status: 200, contentType: 'application/json', body: '{}' }),
138+
);
139+
}
140+
141+
/**
142+
* 指定 predicate にマッチしたリクエスト URL を記録する。
143+
* 呼び出し時点で配列を返し、以降の page 操作中に push される。
144+
* 認証パラメータ注入の検証などに使用。
145+
*/
146+
export function recordRequests(
147+
page: Page,
148+
predicate: (url: string) => boolean = () => true,
149+
): string[] {
150+
const urls: string[] = [];
151+
page.on('request', (req) => {
152+
if (predicate(req.url())) urls.push(req.url());
153+
});
154+
return urls;
155+
}

e2e/sample.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* PR0 sanity-check: helper.ts のヘルパー群が機能していることを確認する最小テスト。
3+
* 後続フェーズで具体的なテストが充実したら削除してよい。
4+
*/
5+
import { test, expect } from '@playwright/test';
6+
import {
7+
TEST_URL,
8+
mockGeoloniaTiles,
9+
waitForStyleLoad,
10+
hasMapInstance,
11+
getStyleLayerIds,
12+
recordRequests,
13+
} from './helper';
14+
15+
test.describe('PR0 helper sanity', () => {
16+
test('mockGeoloniaTiles を適用すると minimal style だけが読み込まれる', async ({ page }) => {
17+
await mockGeoloniaTiles(page);
18+
await page.goto(`${TEST_URL}/nocontrol.html`);
19+
await waitForStyleLoad(page);
20+
21+
expect(await hasMapInstance(page)).toBe(true);
22+
const layers = await getStyleLayerIds(page);
23+
expect(layers).toEqual(['background']);
24+
});
25+
26+
test('recordRequests で geolonia 系へのリクエストを観測できる', async ({ page }) => {
27+
await mockGeoloniaTiles(page);
28+
const styleRequests = recordRequests(page, (url) => url.includes('cdn.geolonia.com/style/'));
29+
await page.goto(`${TEST_URL}/nocontrol.html`);
30+
await waitForStyleLoad(page);
31+
32+
expect(styleRequests.length).toBeGreaterThan(0);
33+
expect(styleRequests[0]).toMatch(/cdn\.geolonia\.com\/style\/.+\.json/);
34+
});
35+
});

playwright.config.ts

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,42 +32,16 @@ export default defineConfig({
3232
trace: 'on-first-retry',
3333
},
3434

35-
/* Configure projects for major browsers */
35+
/*
36+
* embed は maplibre-gl の薄いラッパーであり、クロスブラウザ互換は
37+
* maplibre 本体のテストに委ねる。embed 側の E2E は chromium のみで
38+
* 回し、iteration 速度を優先する。
39+
*/
3640
projects: [
3741
{
3842
name: 'chromium',
3943
use: { ...devices['Desktop Chrome'] },
4044
},
41-
42-
{
43-
name: 'firefox',
44-
use: { ...devices['Desktop Firefox'] },
45-
},
46-
47-
{
48-
name: 'webkit',
49-
use: { ...devices['Desktop Safari'] },
50-
},
51-
52-
/* Test against mobile viewports. */
53-
// {
54-
// name: 'Mobile Chrome',
55-
// use: { ...devices['Pixel 5'] },
56-
// },
57-
// {
58-
// name: 'Mobile Safari',
59-
// use: { ...devices['iPhone 12'] },
60-
// },
61-
62-
/* Test against branded browsers. */
63-
// {
64-
// name: 'Microsoft Edge',
65-
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
66-
// },
67-
// {
68-
// name: 'Google Chrome',
69-
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
70-
// },
7145
],
7246

7347
/* Run your local dev server before starting the tests */

0 commit comments

Comments
 (0)