mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 07:04:04 -05:00
## Summary This PR makes URL serialization logic available to Node.js contexts (like Cypress tests) and replaces a hard-coded 4812-character base64 URL in tests with programmatically generated state. This builds on the shared utilities refactoring from #8246. ### Changes #### 1. Extract URL Serialization to Shared Module **Problem:** URL serialization code depended on GoldenLayout's browser-only ConfigMinifier, preventing Cypress spec files from importing it (they load in Node.js before running in browser). **Solution:** Created `shared/url-serialization.ts` with a Node-compatible ConfigMinifier reimplementation. **Technical Details:** - Reimplemented GoldenLayout's ConfigMinifier without browser dependencies - Moved serialization functions (`serialiseState`, `deserialiseState`, `risonify`, `unrisonify`) to shared module - Moved minification functions (`minifyConfig`, `unminifyConfig`) to shared module - Updated `static/url.ts` to use shared module instead of GoldenLayout - Added comprehensive test coverage in `test/url-serialization.ts` **Files:** - **New:** `shared/url-serialization.ts` (~279 lines) - **Modified:** `static/url.ts` (removed ~30 lines, eliminated GoldenLayout dependency) - **New:** `test/url-serialization.ts` (~96 lines) #### 2. Replace Hard-coded Cypress URL with Programmatic State **Before:** A hard-coded 4812-character base64 URL containing state for all panes ```typescript cy.visit('http://localhost:10240/#z:OYLghAFBqd5TB8IAsQGMD2ATApgUWwEsAXTAJwBoiQIAzIgG...'); ``` **After:** Programmatically generated state using `buildKnownGoodState()` function ```typescript const state = buildKnownGoodState(); const hash = serialiseState(state); cy.visit(`http://localhost:10240/#${hash}`, {...}); ``` **Benefits:** - Human-readable, maintainable test state - Programmatic generation from `PANE_DATA_MAP` keys - Layout optimized with 8 panes per row - Produces identical compressed URL format - Much easier to add/modify panes in the future #### 3. PANE_DATA_MAP Consistency Improvements Updated `PANE_DATA_MAP` to use component names exactly as registered with GoldenLayout: **Key renames:** - `preprocessor` → `pp` - `llvmir` → `ir` - `pipeline` → `llvmOptPipelineView` - `mir` → `rustmir` - `hir` → `rusthir` - `macro` → `rustmacroexp` - `core` → `haskellCore` - `stg` → `haskellStg` - `cmm` → `haskellCmm` - `dump` → `gccdump` - `tree` → `gnatdebugtree` - `debug` → `gnatdebug` **Added panes:** `codeEditor`, `compiler`, `conformance`, `output` (were missing from map) **Re-enabled tests:** - `yul` pane test (was commented out, now fixed) - `clojuremacroexp` pane test (was commented out, now fixed) - `cfg` pane test (had TODO, now removed) **Why this matters:** The `buildKnownGoodState()` function uses `Object.keys(PANE_DATA_MAP)` as the `componentName` property, so keys must match the actual registered component names for GoldenLayout to find them. ## Test Plan - [x] All Cypress tests pass (confirmed by @mattgodbolt) - [x] TypeScript compilation passes (`npm run ts-check`) - [x] Linting passes (`npm run lint`) - [x] URL serialization tests pass (3/3 tests) - [x] Pre-commit hooks pass - [x] Related vitest tests pass ## Dependencies - Builds on #8246 (shared utilities refactoring - already merged) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com>
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
import {describe, expect, it} from 'vitest';
|
|
import * as urlSerialization from '../shared/url-serialization.js';
|
|
|
|
describe('URL Serialization', () => {
|
|
it('should serialise and deserialise a simple state', () => {
|
|
const state = {
|
|
content: [
|
|
{
|
|
type: 'row',
|
|
content: [
|
|
{
|
|
type: 'component',
|
|
componentName: 'codeEditor',
|
|
componentState: {
|
|
id: 1,
|
|
lang: 'c++',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
const serialized = urlSerialization.serialiseState(state);
|
|
expect(serialized).toBeTruthy();
|
|
expect(typeof serialized).toBe('string');
|
|
|
|
const deserialized = urlSerialization.deserialiseState(serialized);
|
|
expect(deserialized).toBeTruthy();
|
|
expect(deserialized.version).toBe(4);
|
|
expect(deserialized.content).toEqual(state.content);
|
|
});
|
|
|
|
it('should compress large states when beneficial', () => {
|
|
const state = {
|
|
content: [
|
|
{
|
|
type: 'row',
|
|
content: Array.from({length: 50}, (_, i) => ({
|
|
type: 'component',
|
|
componentName: 'codeEditor',
|
|
componentState: {
|
|
id: i,
|
|
lang: 'c++',
|
|
source: 'int main() { return 0; }',
|
|
},
|
|
})),
|
|
},
|
|
],
|
|
};
|
|
|
|
const serialized = urlSerialization.serialiseState(state);
|
|
// Compressed format contains {z: ...} which rison-encodes to contain 'z:'
|
|
// This test just verifies the state serializes successfully
|
|
expect(serialized).toBeTruthy();
|
|
expect(typeof serialized).toBe('string');
|
|
expect(serialized.length).toBeGreaterThan(0);
|
|
|
|
// Verify it can be deserialized
|
|
const deserialized = urlSerialization.deserialiseState(serialized);
|
|
expect(deserialized.content).toHaveLength(1);
|
|
expect(deserialized.content[0].content).toHaveLength(50);
|
|
});
|
|
|
|
it('should handle round-trip encoding correctly', () => {
|
|
const state = {
|
|
content: [
|
|
{
|
|
type: 'column',
|
|
content: [
|
|
{
|
|
type: 'stack',
|
|
content: [
|
|
{
|
|
type: 'component',
|
|
componentName: 'compiler',
|
|
componentState: {
|
|
id: 1,
|
|
compiler: 'g132',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
const hash1 = urlSerialization.serialiseState(state);
|
|
const restored = urlSerialization.deserialiseState(hash1);
|
|
const hash2 = urlSerialization.serialiseState(restored);
|
|
|
|
// Round-trip should produce identical hash
|
|
expect(hash2).toBe(hash1);
|
|
});
|
|
});
|