Files
halo-dark-mode-plugin/third-party/darkreader/tests/inject/dynamic/shadow-dom.tests.ts
T

179 lines
7.6 KiB
TypeScript

import '../support/polyfills';
import {DEFAULT_THEME} from '../../../src/defaults';
import {createOrUpdateDynamicTheme, removeDynamicTheme} from '../../../src/inject/dynamic-theme';
import {isFirefox, isSafari} from '../../../src/utils/platform';
import {multiline, timeout} from '../support/test-utils';
const theme = {
...DEFAULT_THEME,
darkSchemeBackgroundColor: 'black',
darkSchemeTextColor: 'white',
};
let container: HTMLElement;
beforeEach(() => {
container = document.body;
container.innerHTML = '';
});
afterEach(() => {
removeDynamicTheme();
container.innerHTML = '';
});
describe('SHADOW DOM', () => {
it('should add static overrides', async () => {
container.innerHTML = multiline(
'<div class="shadow-dom-wrapper"></div>',
);
document.querySelector('.shadow-dom-wrapper')!.attachShadow({mode: 'open'});
createOrUpdateDynamicTheme(theme, null, false);
const shadowRoot = document.querySelector('.shadow-dom-wrapper')!.shadowRoot!;
expect(shadowRoot.firstElementChild!.classList.contains('darkreader--inline')).toBe(true);
expect(shadowRoot.firstElementChild!.nextElementSibling!.classList.contains('darkreader--override')).toBe(true);
expect(shadowRoot.firstElementChild!.nextElementSibling!.nextElementSibling!.classList.contains('darkreader--invert')).toBe(true);
});
it('should override styles', async () => {
container.innerHTML = multiline(
'<div class="shadow-dom-wrapper"></div>',
);
const shadow = document.querySelector('.shadow-dom-wrapper')!.attachShadow({mode: 'open'});
const style = document.createElement('style');
style.classList.add('test-case-style');
shadow.appendChild(style);
style.sheet!.insertRule('h1 { color: gray }');
style.sheet!.insertRule('strong { color: red }');
createOrUpdateDynamicTheme(theme, null, false);
const shadowRoot = document.querySelector('.shadow-dom-wrapper')!.shadowRoot!;
const testCase = shadowRoot.querySelector('.test-case-style')!;
expect(testCase.nextElementSibling!.classList.contains('darkreader--sync')).toBe(true);
expect((testCase.nextElementSibling as HTMLStyleElement).sheet!.cssRules.length).toBe(2);
});
it('should react to DOM changes', async () => {
container.innerHTML = multiline(
'<div class="shadow-dom-wrapper"></div>',
);
const shadow = document.querySelector('.shadow-dom-wrapper')!.attachShadow({mode: 'open'});
createOrUpdateDynamicTheme(theme, null, false);
const style = document.createElement('style');
style.classList.add('test-case-style');
shadow.appendChild(style);
style.sheet!.insertRule('h1 { color: gray }');
style.sheet!.insertRule('strong { color: red }');
await timeout(0);
const shadowRoot = document.querySelector('.shadow-dom-wrapper')!.shadowRoot!;
const testCase = shadowRoot.querySelector('.test-case-style')!;
expect(shadowRoot.firstElementChild!.classList.contains('darkreader--inline')).toBe(true);
expect(shadowRoot.firstElementChild!.nextElementSibling!.classList.contains('darkreader--override')).toBe(true);
expect(testCase.nextElementSibling!.classList.contains('darkreader--sync')).toBe(true);
expect((testCase.nextElementSibling as HTMLStyleElement).sheet!.cssRules.length).toBe(2);
});
it('should override inline styles', async () => {
container.innerHTML = multiline(
'<div class="shadow-dom-wrapper"></div>',
);
const shadow = document.querySelector('.shadow-dom-wrapper')!.attachShadow({mode: 'open'});
createOrUpdateDynamicTheme(theme, null, false);
const standardElement = document.createElement('p');
standardElement.style.color = 'red';
shadow.appendChild(standardElement);
await timeout(0);
const shadowRoot = document.querySelector('.shadow-dom-wrapper')!.shadowRoot!;
expect(getComputedStyle(shadowRoot.querySelector('p')!).color).toBe('rgb(255, 26, 26)');
});
it('should handle defined custom elements', async () => {
container.innerHTML = multiline(
'<custom-element>',
'</custom-element>',
);
class CustomElement extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
const style = document.createElement('style');
style.textContent = 'p { color: pink }';
const paragraph = document.createElement('p');
paragraph.textContent = 'Some text content that should be pink.';
shadowRoot.append(style);
shadowRoot.append(paragraph);
}
}
customElements.define('custom-element', CustomElement);
createOrUpdateDynamicTheme(theme, null, false);
const shadowRoot = document.querySelector('custom-element')!.shadowRoot!;
expect(getComputedStyle(shadowRoot.querySelector('p')!).color).toBe('rgb(255, 198, 208)');
});
it('should react to defined custom elements', async () => {
container.innerHTML = multiline(
'<delayed-custom-element>',
'</delayed-custom-element>',
);
class DelayedCustomElement extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
const style = document.createElement('style');
style.textContent = 'p { color: pink }';
const paragraph = document.createElement('p');
paragraph.textContent = 'Some text content that should be pink.';
shadowRoot.append(style);
shadowRoot.append(paragraph);
}
}
createOrUpdateDynamicTheme(theme, null, false);
customElements.define('delayed-custom-element', DelayedCustomElement);
await timeout(0);
const shadowRoot = document.querySelector('delayed-custom-element')!.shadowRoot!;
expect(getComputedStyle(shadowRoot.querySelector('p')!).color).toBe('rgb(255, 198, 208)');
});
it('should override styles', async () => {
// Firefox by default doesn't enable the CSSStyleSheet constructor.
if (isFirefox || isSafari) {
return;
}
container.innerHTML = multiline(
'<div class="shadow-dom-wrapper"></div>',
);
const newRule = new CSSStyleSheet();
newRule.insertRule(':host { --red: red }');
const shadow = document.querySelector('.shadow-dom-wrapper')!.attachShadow({mode: 'open'});
shadow.adoptedStyleSheets = [newRule];
const style = document.createElement('style');
style.classList.add('test-case-style');
shadow.appendChild(style);
style.sheet!.insertRule('h1 { color: var(--red) }');
const h1 = document.createElement('h1');
shadow.appendChild(h1);
createOrUpdateDynamicTheme(theme, null, false);
const shadowRoot = document.querySelector('.shadow-dom-wrapper')!.shadowRoot!;
const testCase = shadowRoot.querySelector('.test-case-style')!;
const darkendH1 = shadowRoot.querySelector('h1')!;
expect(testCase.nextElementSibling!.classList.contains('darkreader--sync')).toBe(true);
expect((testCase.nextElementSibling as HTMLStyleElement).sheet!.cssRules.length).toBe(1);
expect(shadowRoot.adoptedStyleSheets.length).toBe(2);
expect(getComputedStyle(darkendH1).color).toBe('rgb(255, 26, 26)');
});
});