import '../support/polyfills'; import {DEFAULT_THEME} from '../../../src/defaults'; import {createOrUpdateDynamicTheme, removeDynamicTheme} from '../../../src/inject/dynamic-theme'; import {injectProxy} from '../../../src/inject/dynamic-theme/stylesheet-proxy'; import {isFirefox} from '../../../src/utils/platform'; import {stubChromeRuntimeGetURL} from '../support/background-stub'; import {getJSEchoURL} from '../support/echo-client'; import {multiline, timeout, waitForEvent} from '../support/test-utils'; const theme = { ...DEFAULT_THEME, darkSchemeBackgroundColor: 'black', darkSchemeTextColor: 'white', }; let container: HTMLElement; beforeAll(() => { const loader = multiline( '(function loader() {', ' document && document.currentScript && document.currentScript.remove();', ' const argString = document && document.currentScript && document.currentScript.dataset.arg;', ' if (argString !== undefined) {', ' const arg = JSON.parse(argString);', ` (${injectProxy.toString()})(arg);`, ' }', '})()', ); const url = getJSEchoURL(loader); stubChromeRuntimeGetURL('inject/proxy.js', url); }); beforeEach(() => { container = document.body; container.innerHTML = ''; }); afterEach(() => { removeDynamicTheme(); container.innerHTML = ''; document.documentElement.removeAttribute('style'); }); describe('CSS VARIABLES OVERRIDE', () => { it('should override style with variables', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container).backgroundColor).toBe('rgb(0, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(102, 102, 102)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('h1 strong')!).color).toBe('rgb(255, 26, 26)'); }); it('should override style with variables(that contain spaces)', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container).backgroundColor).toBe('rgb(0, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(102, 102, 102)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('h1 strong')!).color).toBe('rgb(255, 26, 26)'); }); it('should override style with variables (reverse order)', () => { container.innerHTML = multiline( '', '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container).backgroundColor).toBe('rgb(0, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(102, 102, 102)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('h1 strong')!).color).toBe('rgb(140, 255, 140)'); }); it('should skip undefined variables', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container).backgroundColor).toBe('rgb(0, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgba(0, 0, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('h1 strong')!).color).toBe('rgb(255, 255, 255)'); }); it('should use fallback variable value', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); }); it('should not use fallback variable value', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should handle variables referencing other variables', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); }); it('should handle variables referencing other variables (reverse order)', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); }); it('should handle shorthand background with deep color refs', () => { container.innerHTML = multiline( '', '

CSS variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); }); it('should handle background with deep color refs (backwards)', () => { container.innerHTML = multiline( '', '

CSS variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); }); it('should handle multi-type vars with deep color refs', () => { container.innerHTML = multiline( '', '

CSS variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); }); it('should not hang on exponentially expanding variable references', () => { const depth = 30; const varLines: string[] = []; for (let i = 1; i < depth; i++) { varLines.push(` --x${i}: var(--x${i + 1}) var(--x${i + 1});`); } varLines.push(` --x${depth}: #770000;`); container.innerHTML = multiline( '', '

CSS variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); }); it('should handle variables having multiple types', () => { container.innerHTML = multiline( '', '

Dark background light text

', '

Light background dark text

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('.dark')!).backgroundColor).toBe('rgb(204, 0, 0)'); expect(getComputedStyle(container.querySelector('.dark')!).color).toBe('rgb(140, 255, 140)'); expect(getComputedStyle(container.querySelector('.light')!).backgroundColor).toBe('rgb(0, 102, 0)'); expect(getComputedStyle(container.querySelector('.light')!).color).toBe('rgb(255, 26, 26)'); }); /* it('should handle variables having multiple types in shorthand properties', () => { container.innerHTML = multiline( '', '

Variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundImage).toBe('linear-gradient(rgb(0, 102, 0), rgb(0, 0, 0))'); expect(getComputedStyle(container.querySelector('h1')!).borderColor).toBe('rgb(0, 217, 0)'); }); */ it('should use fallback when nested variables are missing', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should not freeze on cyclic references', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); }); it('should not freeze on nested cyclic references', () => { container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); }); it('should react on variable change', async () => { container.innerHTML = multiline( '', '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); const styleElement = document.getElementById('variables') as HTMLStyleElement; styleElement.textContent = ':root { --text: green; }'; await timeout(0); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should use element variables', async () => { document.documentElement.setAttribute('style', '--text: red;'); container.innerHTML = multiline( '', '

CSS variables!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); document.documentElement.setAttribute('style', '--text: green;'); await timeout(0); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should consider variable selector', () => { container.innerHTML = multiline( '', '

Red CSS variable

', '

Green CSS variable

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('.red')!).color).toBe('rgb(255, 26, 26)'); expect(getComputedStyle(container.querySelector('.green')!).color).toBe('rgb(140, 255, 140)'); }); it('should handle internal conversion of hex to RGB', async () => { container.innerHTML = multiline( '', '', '

Dark Theme!

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 0, 0)'); }); it('should handle variables inside color values (constructed colors)', async () => { container.innerHTML = multiline( '', '', '

Colors with variables inside

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 0, 0)'); }); it('should use fallback when variable inside a color not found', async () => { container.innerHTML = multiline( '', '

Colors with variables inside

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); }); it('should handle variables in constructed colors that refer to other vars', async () => { container.innerHTML = multiline( '', '', '

Colors with variables inside

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); }); it('should handle variables that refer to constructed colors', async () => { container.innerHTML = multiline( '', '', '

Colors with variables inside

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(51, 125, 255)'); }); it('should handle variables that refer to constructed colors asynchronously', async () => { container.innerHTML = multiline( '', '

Colors with variables inside

', ); createOrUpdateDynamicTheme(theme, null, false); const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( ':root {', ' --blue: 0, 0, 255;', ' --rgb-blue: rgb(var(--blue));', ' --text: var(--rgb-blue);', '}', 'h1 {', ' background: var(--bg);', '}', ); container.append(anotherStyle); await timeout(0); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(51, 125, 255)'); }); it('should handle variables that are both contructed and usual colors', async () => { container.innerHTML = multiline( '', '

Colors with variables inside

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); }); it('should handle cyclic references in constructed colors', async () => { container.innerHTML = multiline( '', '', '

Colors with variables inside

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 0, 0)'); }); it('should handle variables inside border color values', async () => { container.innerHTML = multiline( '', '', '

Colors with variables inside

', ); createOrUpdateDynamicTheme(theme, null, false); const elementStyle = getComputedStyle(container.querySelector('h1')!); if (isFirefox) { expect(elementStyle.borderTopColor).toBe('rgb(179, 0, 0)'); expect(elementStyle.borderRightColor).toBe('rgb(179, 0, 0)'); expect(elementStyle.borderBottomColor).toBe('rgb(179, 0, 0)'); expect(elementStyle.borderLeftColor).toBe('rgb(179, 0, 0)'); } else { expect(elementStyle.borderColor).toBe('rgb(179, 0, 0)'); } }); it('should handle media variables', () => { container.innerHTML = multiline( '', '

Red CSS variable

', '

Green CSS variable

', '

Orange CSS variable

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container).backgroundColor).toBe('rgb(0, 0, 0)'); expect(getComputedStyle(container.querySelector('.red')!).color).toBe('rgb(255, 26, 26)'); expect(getComputedStyle(container.querySelector('.green')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('.orange')!).color).toBe('rgb(255, 174, 26)'); }); it('should handle nested variables', () => { container.innerHTML = multiline( '', '

Red CSS variable

', '

Green CSS variable

', '

Orange CSS variable

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container).backgroundColor).toBe('rgb(0, 0, 0)'); expect(getComputedStyle(container.querySelector('.red')!).color).toBe('rgb(255, 26, 26)'); expect(getComputedStyle(container.querySelector('.green')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('.orange')!).color).toBe('rgb(255, 255, 255)'); }); it('should handle media with the same selectors', () => { container.innerHTML = multiline( '', '

Media with same selectors

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should preserve media after change', () => { container.innerHTML = multiline( '', '

Media after change

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should handle same selector with different variables', () => { container.innerHTML = multiline( '', '

Media with same selectors

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(204, 0, 0)'); }); it('should properly modify variables in light mode', () => { container.innerHTML = multiline( '', '

Light scheme

', ); const lightTheme = {...theme, mode: 0, lightSchemeBackgroundColor: '#dddddd', lightSchemeTextColor: '#222222'}; createOrUpdateDynamicTheme(lightTheme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(221, 221, 221)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(34, 34, 34)'); }); it('should handle variables inside values', () => { container.innerHTML = multiline( '', '

Border with variable

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).borderBottomColor).toBe('rgb(179, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should handle variables that have variables inside', () => { container.innerHTML = multiline( '', '

Border with variable

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).borderTopColor).toBe('rgb(179, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).borderBottomColor).toBe('rgb(179, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).borderLeftColor).toBe('rgb(179, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).borderRightColor).toBe('rgb(179, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should handle border color variables', () => { container.innerHTML = multiline( '', '

Border color with variable

', ); createOrUpdateDynamicTheme(theme, null, false); const elementStyle = getComputedStyle(container.querySelector('h1')!); if (isFirefox) { expect(elementStyle.borderTopColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderRightColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderBottomColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderLeftColor).toBe('rgb(0, 217, 0)'); } else { expect(elementStyle.borderColor).toBe('rgb(0, 217, 0)'); } }); it('should handle variables with gradients', () => { container.innerHTML = multiline( '', '

Weow Gradients

', '

Gradient 2

' ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 102, 0)'); expect(getComputedStyle(container.querySelector('h1')!).backgroundImage).toBe('linear-gradient(rgb(204, 0, 0), rgb(0, 0, 0))'); expect(getComputedStyle(container.querySelector('h2')!).backgroundImage).toBe('linear-gradient(rgb(204, 0, 0), rgb(0, 0, 0))'); }); it('should handle variables with background images', async () => { const darkIcon = multiline( '', ' ', '', ); const redCross = multiline( '', ' ', '', ); container.innerHTML = multiline( '', '

', ' ', ' ', ' ', ' Icons', '

', ); createOrUpdateDynamicTheme(theme, null, false); await waitForEvent('__darkreader__test__asyncQueueComplete'); expect(getComputedStyle(container.querySelector('.icon1')!).backgroundImage).toMatch(/^url\("blob:.*"\)$/); expect(getComputedStyle(container.querySelector('.icon2')!).backgroundImage).toMatch(/^url\("blob:.*"\)$/); expect(getComputedStyle(container.querySelector('.icon3')!).backgroundImage).toMatch(/^url\("blob:.*"\), url\("blob:.*"\)$/); }); it('should handle variables with gradients and images', async () => { const darkIcon = multiline( '', ' ', '', ); container.innerHTML = multiline( '', '

Mixed background

', ); createOrUpdateDynamicTheme(theme, null, false); await waitForEvent('__darkreader__test__asyncQueueComplete'); expect(getComputedStyle(container.querySelector('.icon')!).backgroundImage).toMatch(/^url\("blob:.*"\), linear-gradient\(rgb\(204, 0, 0\), rgb\(0, 0, 0\)\)$/); }); it('should handle asynchronous variable type resolution', async () => { container.innerHTML = multiline( '', '

Asynchronous variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgba(0, 0, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( 'h1 {', ' background: var(--color2);', ' color: var(--color1);', '}', ); container.append(anotherStyle); await timeout(0); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 102, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); }); it('should handle async resolution of multiple variable types', async () => { container.innerHTML = multiline( '', '

Asynchronous variables with multiple types

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgba(0, 0, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( 'h1 {', ' background: var(--color);', ' border: 1px solid var(--color);', ' color: var(--color);', '}', ); container.append(anotherStyle); await timeout(0); const updatedStyle = getComputedStyle(container.querySelector('h1')!); expect(updatedStyle.backgroundColor).toBe('rgb(0, 102, 0)'); expect(updatedStyle.color).toBe('rgb(140, 255, 140)'); if (isFirefox) { expect(updatedStyle.borderTopColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderRightColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderBottomColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderLeftColor).toBe('rgb(0, 217, 0)'); } else { expect(updatedStyle.borderColor).toBe('rgb(0, 217, 0)'); } }); it('should handle variable type resolution when style changed', async () => { container.innerHTML = multiline( '', '

Asynchronous variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgba(0, 0, 0, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); const styleEl = container.querySelector('style')!; styleEl.textContent = multiline( ':root {', ' --color1: red;', ' --color2: green;', ' --color3: blue;', '}', ); await timeout(0); const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( 'h1 {', ' background: var(--color2);', ' color: var(--color1);', '}', ); container.append(anotherStyle); await timeout(0); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 102, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); }); it('should handle variable type change', async () => { container.innerHTML = multiline( '', '

Asynchronous variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 102, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(255, 255, 255)'); const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( 'h1 {', ' color: var(--color);', '}', ); container.append(anotherStyle); await timeout(0); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 102, 0)'); expect(getComputedStyle(container.querySelector('h1')!).color).toBe('rgb(140, 255, 140)'); }); it('should rebuild dependant variable rule when type becomes known', async () => { container.innerHTML = multiline( '', '

Asynchronous variables

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgba(0, 0, 0, 0)'); const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( ':root {', ' --color: green;', '}', ); container.append(anotherStyle); await timeout(50); expect(getComputedStyle(container.querySelector('h1')!).backgroundColor).toBe('rgb(0, 102, 0)'); }); it('should not affect other declarations when variable type resolved', async () => { container.innerHTML = multiline( '', '

Variables with color 1

', '

Variables with color 2

', ); createOrUpdateDynamicTheme(theme, null, false); expect(getComputedStyle(container.querySelector('.color1')!).backgroundColor).toBe('rgba(0, 0, 0, 0)'); expect(getComputedStyle(container.querySelector('.color1')!).color).toBe('rgb(255, 255, 255)'); expect(getComputedStyle(container.querySelector('.color2')!).backgroundColor).toBe('rgba(0, 0, 0, 0)'); expect(getComputedStyle(container.querySelector('.color2')!).color).toBe('rgb(255, 255, 255)'); const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( '.color1 {', ' background: var(--color1);', ' color: var(--color1);', '}', '.color2 {', ' background: var(--color2);', ' color: var(--color2);', '}', ); container.append(anotherStyle); await timeout(0); expect(getComputedStyle(container.querySelector('.color1')!).backgroundColor).toBe('rgb(204, 0, 0)'); expect(getComputedStyle(container.querySelector('.color1')!).color).toBe('rgb(255, 26, 26)'); expect(getComputedStyle(container.querySelector('.color2')!).backgroundColor).toBe('rgb(0, 102, 0)'); expect(getComputedStyle(container.querySelector('.color2')!).color).toBe('rgb(140, 255, 140)'); }); it('should not affect other declarations when variable type resolved', async () => { container.innerHTML = multiline( '', '

Variables along with other declarations

', ); createOrUpdateDynamicTheme(theme, null, false); const elementStyle = getComputedStyle(container.querySelector('h1')!); expect(elementStyle.backgroundColor).toBe('rgba(0, 0, 0, 0)'); expect(elementStyle.color).toBe('rgb(255, 255, 255)'); if (isFirefox) { expect(elementStyle.borderTopColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderRightColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderBottomColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderLeftColor).toBe('rgb(0, 217, 0)'); } else { expect(elementStyle.borderColor).toBe('rgb(0, 217, 0)'); } const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( 'h1 {', ' background-color: var(--color-bg);', ' color: var(--color-text);', '}', ); container.append(anotherStyle); await timeout(0); const updatedStyle = getComputedStyle(container.querySelector('h1')!); expect(updatedStyle.backgroundColor).toBe('rgb(204, 0, 0)'); expect(updatedStyle.color).toBe('rgb(140, 255, 140)'); if (isFirefox) { expect(updatedStyle.borderTopColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderRightColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderBottomColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderLeftColor).toBe('rgb(0, 217, 0)'); } else { expect(updatedStyle.borderColor).toBe('rgb(0, 217, 0)'); } }); it('should not affect other declarations when dependency variable type resolved', async () => { container.innerHTML = multiline( '', '

Dependency variable

', ); createOrUpdateDynamicTheme(theme, null, false); const elementStyle = getComputedStyle(container.querySelector('h1')!); expect(elementStyle.backgroundColor).toBe('rgba(0, 0, 0, 0)'); expect(elementStyle.color).toBe('rgb(255, 26, 26)'); if (isFirefox) { expect(elementStyle.borderTopColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderRightColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderBottomColor).toBe('rgb(0, 217, 0)'); expect(elementStyle.borderLeftColor).toBe('rgb(0, 217, 0)'); } else { expect(elementStyle.borderColor).toBe('rgb(0, 217, 0)'); } const anotherStyle = document.createElement('style'); anotherStyle.textContent = multiline( ':root {', ' --color: green;', '}', ); container.append(anotherStyle); await timeout(50); const updatedStyle = getComputedStyle(container.querySelector('h1')!); expect(updatedStyle.backgroundColor).toBe('rgb(0, 102, 0)'); expect(updatedStyle.color).toBe('rgb(255, 26, 26)'); if (isFirefox) { expect(updatedStyle.borderTopColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderRightColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderBottomColor).toBe('rgb(0, 217, 0)'); expect(updatedStyle.borderLeftColor).toBe('rgb(0, 217, 0)'); } else { expect(updatedStyle.borderColor).toBe('rgb(0, 217, 0)'); } }); it('should add variables to root after the variable type is discovered', async () => { document.documentElement.setAttribute('style', '--text: red;'); container.innerHTML = multiline( '', '

Dependency variable

', ); createOrUpdateDynamicTheme(theme, null, false); const sheet = (document.querySelector('.testcase-sheet') as HTMLStyleElement).sheet!; sheet.insertRule('h1 { color: var(--text);'); await timeout(0); expect(getComputedStyle(document.querySelector('h1')!).color).toBe('rgb(255, 26, 26)'); }); it('should modify the caret-color property', async () => { container.innerHTML = multiline( '', '

Caret Color

', ); createOrUpdateDynamicTheme(theme, null, false); await timeout(0); const elementStyle = getComputedStyle(container.querySelector('h1')!); expect(elementStyle.caretColor).toBe('rgb(140, 255, 140)'); }); it('should modify the box-shadow actual color values in a variable', async () => { container.innerHTML = multiline( '', '

COmplicated shit :(

', ); createOrUpdateDynamicTheme(theme, null, false); await timeout(0); const elementStyle = getComputedStyle(container.querySelector('h1')!); expect(elementStyle.boxShadow).toBe('rgb(0, 0, 0) -9px 0px 0px 0px'); }); it(`shouldn't modify the raw value of box-shadow when their is no color`, async () => { container.innerHTML = multiline( '', '

COmplicated shit :(

', ); createOrUpdateDynamicTheme(theme, null, false); await timeout(0); const elementStyle = getComputedStyle(container.querySelector('h1')!); expect(elementStyle.boxShadow).toBe('rgb(0, 102, 0) 0px -1px 0px 0px inset'); }); it('should handle raw values within variable declarations and use proper replacement', async () => { container.innerHTML = multiline( '', '

Raw values are spooky

', ); createOrUpdateDynamicTheme(theme, null, false); await timeout(0); const elementStyle = getComputedStyle(container.querySelector('h1')!); if (isFirefox) { expect(elementStyle.borderTopColor).toBe('rgb(91, 91, 91)'); expect(elementStyle.borderRightColor).toBe('rgb(91, 91, 91)'); expect(elementStyle.borderBottomColor).toBe('rgb(91, 91, 91)'); expect(elementStyle.borderLeftColor).toBe('rgb(91, 91, 91)'); } else { expect(elementStyle.borderColor).toBe('rgb(91, 91, 91)'); } }); it('should modify inline variable', () => { container.innerHTML = multiline( '', '

Raw values are spooky

', ); createOrUpdateDynamicTheme(theme, null, false); const elementStyle = getComputedStyle(container.querySelector('h1')!); expect(elementStyle.backgroundColor).toBe('rgb(204, 0, 0)'); }); });