-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathtest-customize-theme-e2e.js
More file actions
148 lines (129 loc) · 6.9 KB
/
test-customize-theme-e2e.js
File metadata and controls
148 lines (129 loc) · 6.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
* E2E (#1297 B4): Customizer V2 — Theme tokens + presets + dark/light mode
*
* Exercises the theme-tab subsystem of public/customize-v2.js:
* - Open customizer panel via #customizeToggle
* - Click a preset button → assert CSS variable on documentElement updates
* AND localStorage 'cs-theme-overrides' is written
* - Change a basic color picker → assert THEME_CSS_MAP[key] CSS var updates
* (verifies "all colors via CSS variables" invariant — NOT inline styles)
* - Reload page → assert override persists and CSS var still applied
*
* Usage: BASE_URL=http://localhost:13581 node test-customize-theme-e2e.js
*/
'use strict';
const { chromium } = require('playwright');
const BASE = process.env.BASE_URL || 'http://localhost:3000';
let passed = 0, failed = 0;
async function step(name, fn) {
try { await fn(); passed++; console.log(' \u2713 ' + name); }
catch (e) { failed++; console.error(' \u2717 ' + name + ': ' + e.message); }
}
function assert(c, m) { if (!c) throw new Error(m || 'assertion failed'); }
(async () => {
const browser = await chromium.launch({
headless: true,
executablePath: process.env.CHROMIUM_PATH || undefined,
args: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
});
const ctx = await browser.newContext();
const page = await ctx.newPage();
page.setDefaultTimeout(8000);
page.on('pageerror', (e) => console.error('[pageerror]', e.message));
console.log(`\n=== #1297 B4 customize-theme E2E against ${BASE} ===`);
await step('navigate home and wait for customizer API', async () => {
await page.goto(BASE + '/', { waitUntil: 'domcontentloaded' });
await page.evaluate(() => localStorage.removeItem('cs-theme-overrides'));
await page.reload({ waitUntil: 'load' });
await page.waitForFunction(() => window._customizerV2 && window._customizerV2.initDone, null, { timeout: 8000 });
});
await step('customizer toggle button is wired', async () => {
const btn = await page.$('#customizeToggle');
assert(btn, '#customizeToggle button missing in nav');
});
await step('click toggle opens .cust-overlay panel', async () => {
await page.click('#customizeToggle');
await page.waitForSelector('.cust-overlay:not(.hidden)', { timeout: 4000 });
const visible = await page.isVisible('.cust-overlay');
assert(visible, 'customizer overlay should be visible');
});
await step('theme tab renders preset buttons', async () => {
// Switch to theme tab if needed
const tabBtn = await page.$('.cust-tab[data-tab="theme"]');
if (tabBtn) await tabBtn.click();
const presets = await page.$$('.cust-preset-btn[data-preset]');
assert(presets.length >= 2, 'expected multiple presets, got ' + presets.length);
});
await step('clicking preset writes localStorage AND updates CSS vars (not inline styles)', async () => {
// Find a non-active preset
const presetIds = await page.$$eval('.cust-preset-btn[data-preset]', els =>
els.filter(e => !e.classList.contains('active')).map(e => e.getAttribute('data-preset'))
);
assert(presetIds.length > 0, 'need at least one non-active preset');
const presetId = presetIds[0];
await page.click('.cust-preset-btn[data-preset="' + presetId + '"]');
await page.waitForTimeout(400); // debounced write
// Assert localStorage
const raw = await page.evaluate(() => localStorage.getItem('cs-theme-overrides'));
assert(raw, 'cs-theme-overrides not written after preset click');
const parsed = JSON.parse(raw);
assert(parsed.theme || parsed.themeDark, 'preset write should include theme section, got ' + raw);
// Assert CSS var on :root reflects new accent (vs hardcoded inline style)
const accentVar = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--accent').trim()
);
assert(accentVar.length > 0, '--accent CSS variable should be set on documentElement');
assert(accentVar.startsWith('#') || accentVar.startsWith('rgb'),
'--accent should be a color value, got: ' + accentVar);
});
await step('color picker change updates CSS variable (THEME_CSS_MAP invariant)', async () => {
// Find the accent color picker — determine which section is active (theme vs themeDark)
const pickerInfo = await page.evaluate(() => {
const el = document.querySelector('input[data-cv2-field="theme.accent"], input[data-cv2-field="themeDark.accent"]');
if (!el) return null;
return { section: el.dataset.cv2Field.split('.')[0] };
});
assert(pickerInfo, 'accent color picker not found');
const themeKey = pickerInfo.section; // 'theme' or 'themeDark'
// Set a known color via the input event
await page.evaluate((sel) => {
const el = document.querySelector(sel);
el.value = '#ff00aa';
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
}, 'input[data-cv2-field="' + themeKey + '.accent"]');
await page.waitForTimeout(400);
// CSS variable should be updated
const accentVal = await page.evaluate(() =>
document.documentElement.style.getPropertyValue('--accent').trim()
);
assert(accentVal === '#ff00aa', 'expected inline --accent=#ff00aa on documentElement, got ' + accentVal);
// localStorage should reflect the override in the *active* section
const raw = await page.evaluate(() => localStorage.getItem('cs-theme-overrides'));
const parsed = JSON.parse(raw);
assert(parsed[themeKey] && parsed[themeKey].accent === '#ff00aa',
'localStorage[' + themeKey + '].accent should be #ff00aa, got ' + raw);
// Save the key for the persist-across-reload assertion
page._themeKey = themeKey;
});
await step('override persists across reload', async () => {
await page.reload({ waitUntil: 'load' });
await page.waitForFunction(() => window._customizerV2 && window._customizerV2.initDone, null, { timeout: 8000 });
const accentVal = await page.evaluate(() =>
document.documentElement.style.getPropertyValue('--accent').trim()
);
assert(accentVal === '#ff00aa', 'expected --accent to persist as #ff00aa after reload, got: ' + accentVal);
const raw = await page.evaluate(() => localStorage.getItem('cs-theme-overrides'));
const parsed = JSON.parse(raw);
// After reload, the override may be in theme or themeDark depending on active mode
const themeKey = (parsed.themeDark && parsed.themeDark.accent === '#ff00aa') ? 'themeDark' :
(parsed.theme && parsed.theme.accent === '#ff00aa') ? 'theme' : null;
assert(themeKey, 'persisted override missing — neither theme.accent nor themeDark.accent is #ff00aa, got: ' + raw);
});
await step('cleanup: clear overrides', async () => {
await page.evaluate(() => localStorage.removeItem('cs-theme-overrides'));
});
await browser.close();
console.log('\n' + passed + '/' + (passed + failed) + ' tests passed');
process.exit(failed > 0 ? 1 : 0);
})();