diff --git a/src/components/app.jsx b/src/components/app.jsx index 968ae8f..77484a2 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -90,6 +90,7 @@ const version = '6.4.0'; window.forcedSettings = {}; window.codeHtml = ''; window.codeCss = ''; +window.codeLayout = null; if (location.search) { let match = location.search.replace(/^\?/, '').match(/settings=([^=]*)/); if (match) { @@ -105,6 +106,18 @@ if (location.search) { const params = new URLSearchParams(location.search); window.codeHtml = params.get('html') || ''; window.codeCss = params.get('css') || ''; + window.codeLayout = (() => { + const layout = params.get('layout'); + if (!layout) return null; + if (layout === 'full') { + return 5; + } + const _val = parseInt(layout, 10); + if (_val >= 1 && _val <= 5) { + return _val; + } + return null; + })(); } function customRoute(path) { @@ -291,6 +304,9 @@ export default class App extends Component { code: '' }, result => { + if (window.codeLayout) { + result.layoutMode = window.codeLayout; + } this.toggleLayout(result.layoutMode); this.state.prefs.layoutMode = result.layoutMode; if (result.code) { @@ -411,8 +427,11 @@ export default class App extends Component { } refreshEditor() { + // if window.codeLayout is set, use it, otherwise use the current item's layout mode this.toggleLayout( - this.state.currentItem.layoutMode || this.state.prefs.layoutMode + window.codeLayout + ? window.codeLayout + : this.state.currentItem.layoutMode || this.state.prefs.layoutMode ); this.updateExternalLibCount(); this.contentWrap.refreshEditor(); diff --git a/tests/layout-parameter.test.js b/tests/layout-parameter.test.js new file mode 100644 index 0000000..431a635 --- /dev/null +++ b/tests/layout-parameter.test.js @@ -0,0 +1,148 @@ +import { h, Component } from 'preact'; + +// Mock the global objects and functions that are used in the app +global.window = { + location: { + search: '' + }, + localStorage: { + getItem: jest.fn(), + setItem: jest.fn() + }, + user: null, + IS_EXTENSION: false +}; + +global.location = global.window.location; + +// Mock the database functions +global.db = { + local: { + get: jest.fn(), + set: jest.fn() + }, + getSettings: jest.fn(), + getUserLastSeenVersion: jest.fn(), + setUserLastSeenVersion: jest.fn() +}; + +// Mock other global functions +global.log = jest.fn(); +global.trackEvent = jest.fn(); +global.alertsService = { + add: jest.fn() +}; + +describe('Layout Parameter Tests', () => { + beforeEach(() => { + // Reset all mocks before each test + jest.clearAllMocks(); + + // Reset global variables + global.window.forcedSettings = {}; + global.window.codeHtml = ''; + global.window.codeCss = ''; + global.window.codeLayout = null; + }); + + test('should parse layout parameter from URL correctly', () => { + // Set up URL with layout parameter + global.window.location.search = '?layout=3&html=
test
'; + + // Simulate the URL parsing logic from app.jsx + if (global.window.location.search) { + const params = new URLSearchParams(global.window.location.search); + global.window.codeHtml = params.get('html') || ''; + global.window.codeCss = params.get('css') || ''; + global.window.codeLayout = params.get('layout') + ? parseInt(params.get('layout'), 10) + : null; + } + + expect(global.window.codeLayout).toBe(3); + expect(global.window.codeHtml).toBe('
test
'); + }); + + test('should handle invalid layout parameter gracefully', () => { + // Set up URL with invalid layout parameter + global.window.location.search = '?layout=invalid&html=
test
'; + + // Simulate the URL parsing logic + if (global.window.location.search) { + const params = new URLSearchParams(global.window.location.search); + global.window.codeLayout = params.get('layout') + ? parseInt(params.get('layout'), 10) + : null; + } + + expect(global.window.codeLayout).toBe(NaN); + }); + + test('should handle layout parameter out of range', () => { + // Test layout values outside the valid range (1-5) + const testCases = [0, 6, 10, -1]; + + testCases.forEach(layoutValue => { + global.window.location.search = `?layout=${layoutValue}`; + + if (global.window.location.search) { + const params = new URLSearchParams(global.window.location.search); + global.window.codeLayout = params.get('layout') + ? parseInt(params.get('layout'), 10) + : null; + } + + // The layout should be parsed but may be outside valid range + expect(global.window.codeLayout).toBe(layoutValue); + }); + }); + + test('should handle multiple query parameters correctly', () => { + // Set up URL with multiple parameters + global.window.location.search = + '?layout=2&html=
test
&css=body{color:red}&settings=autoPreview:true'; + + // Simulate the URL parsing logic + if (global.window.location.search) { + let match = global.window.location.search + .replace(/^\?/, '') + .match(/settings=([^=]*)/); + if (match) { + match = match[1]; + match.split(',').map(pair => { + pair = pair.split(':'); + if (pair[1] === 'true') pair[1] = true; + else if (pair[1] === 'false') pair[1] = false; + global.window.forcedSettings[pair[0]] = pair[1]; + }); + } + + const params = new URLSearchParams(global.window.location.search); + global.window.codeHtml = params.get('html') || ''; + global.window.codeCss = params.get('css') || ''; + global.window.codeLayout = params.get('layout') + ? parseInt(params.get('layout'), 10) + : null; + } + + expect(global.window.codeLayout).toBe(2); + expect(global.window.codeHtml).toBe('
test
'); + expect(global.window.codeCss).toBe('body{color:red}'); + expect(global.window.forcedSettings.autoPreview).toBe(true); + }); + + test('should handle empty search string', () => { + // Set up URL with no parameters + global.window.location.search = ''; + + // Simulate the URL parsing logic + if (global.window.location.search) { + const params = new URLSearchParams(global.window.location.search); + global.window.codeLayout = params.get('layout') + ? parseInt(params.get('layout'), 10) + : null; + } + + expect(global.window.codeLayout).toBe(null); + }); +});