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)');
});
});