test(app): remove useless frontend tests (#2819)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-17 15:05:36 +02:00
1 parent ed9528834d
commit deac653a9e
341 files changed
-62758

No files matched your search

@@ -1,232 +0,0 @@
// @vitest-environment happy-dom
// SPDX-License-Identifier: AGPL-3.0-or-later
import {commitUnderTranslation, isTranslationDomCrash} from '@app/features/i18n/testing/TranslationCommitHarness';
import {
chromeFaithfulCompoundPipeline,
findTranslationUnsafeTextNodes,
TRANSLATION_MARKER,
TRANSLATOR_PIPELINE_NAMES,
TranslatorPipelines,
} from '@app/features/i18n/testing/TranslatorSimulation';
import {createElement, Fragment, type ReactNode} from 'react';
import {afterEach, describe, expect, it} from 'vitest';
const badge = (on: boolean): ReactNode => (on ? createElement('b', null, '!') : null);
const tail = createElement('i', null, 'x');
const BareLabel = (): ReactNode => 'Hello';
const WrappedLabel = (): ReactNode => createElement('flx-i18n', null, 'Hello');
const wrap = (text: string): ReactNode => createElement('flx-i18n', null, text);
const row = (...children: Array<ReactNode>): ReactNode => createElement('div', null, ...children);
const visibleText = (html: string): string => html.replace(/<[^>]+>/g, '').replaceAll(TRANSLATION_MARKER, '');
interface Scenario {
name: string;
unsafe: (on: boolean) => ReactNode;
fixed: (on: boolean) => ReactNode;
beforeState: boolean;
afterState: boolean;
expectedText: string;
expectedFirstTag: string;
}
const scenarios: Array<Scenario> = [
{
name: 'placement before a bare text sibling',
unsafe: (on) => row(badge(on), 'Hello', tail),
fixed: (on) => row(badge(on), wrap('Hello'), tail),
beforeState: false,
afterState: true,
expectedText: '!Hellox',
expectedFirstTag: 'B',
},
{
name: 'deletion of a bare text sibling',
unsafe: (on) => row(on ? 'Hello' : null, tail),
fixed: (on) => row(on ? wrap('Hello') : null, tail),
beforeState: true,
afterState: false,
expectedText: 'x',
expectedFirstTag: 'I',
},
{
name: 'placement between adjacent text nodes',
unsafe: (on) => row('A', badge(on), 'B', tail),
fixed: (on) => row(wrap('A'), badge(on), wrap('B'), tail),
beforeState: false,
afterState: true,
expectedText: 'A!Bx',
expectedFirstTag: 'FLX-I18N',
},
{
name: 'placement before text inside a fragment',
unsafe: (on) => row(createElement(Fragment, null, badge(on), 'Hello'), tail),
fixed: (on) => row(createElement(Fragment, null, badge(on), wrap('Hello')), tail),
beforeState: false,
afterState: true,
expectedText: '!Hellox',
expectedFirstTag: 'B',
},
{
name: 'placement before a component that returns a bare string',
unsafe: (on) => row(badge(on), createElement(BareLabel), tail),
fixed: (on) => row(badge(on), createElement(WrappedLabel), tail),
beforeState: false,
afterState: true,
expectedText: '!Hellox',
expectedFirstTag: 'B',
},
];
afterEach(() => {
document.body.replaceChildren();
});
for (const pipelineName of TRANSLATOR_PIPELINE_NAMES) {
const pipeline = TranslatorPipelines[pipelineName];
describe(`${pipelineName} pipeline`, () => {
for (const scenario of scenarios) {
it(`throws the production DOM error on ${scenario.name}`, () => {
const result = commitUnderTranslation({
before: scenario.unsafe(scenario.beforeState),
after: scenario.unsafe(scenario.afterState),
pipeline,
});
expect(result.unsafeTextNodesBeforeTranslation).toBeGreaterThan(0);
expect(result.htmlAfterTranslation).not.toBe(result.htmlBeforeTranslation);
expect(isTranslationDomCrash(result.error)).toBe(true);
});
if (pipelineName === 'chromeCompound') {
it(`leaves the compound pipeline no text-and-element parent to rewrite in ${scenario.name}`, () => {
const unsafe = commitUnderTranslation({
before: scenario.unsafe(scenario.beforeState),
after: scenario.unsafe(scenario.beforeState),
pipeline,
});
expect(unsafe.unsafeTextNodesBeforeTranslation).toBeGreaterThan(0);
expect(unsafe.htmlAfterTranslation).not.toBe(unsafe.htmlBeforeTranslation);
const fixed = commitUnderTranslation({
before: scenario.fixed(scenario.beforeState),
after: scenario.fixed(scenario.afterState),
pipeline,
});
expect(fixed.unsafeTextNodesBeforeTranslation).toBe(0);
expect(fixed.htmlAfterTranslation).toBe(fixed.htmlBeforeTranslation);
expect(fixed.error).toBeNull();
});
continue;
}
it(`survives ${scenario.name} once the text is a sole child`, () => {
let firstTag: string | null = null;
let unsafeTextNodes = -1;
const result = commitUnderTranslation({
before: scenario.fixed(scenario.beforeState),
after: scenario.fixed(scenario.afterState),
pipeline,
inspect: (host) => {
firstTag = host.querySelector('div')?.firstElementChild?.tagName ?? null;
unsafeTextNodes = findTranslationUnsafeTextNodes(host).length;
},
});
expect(result.htmlAfterTranslation).not.toBe(result.htmlBeforeTranslation);
expect(result.htmlAfterTranslation).toContain(TRANSLATION_MARKER);
expect(result.error).toBeNull();
expect(result.unsafeTextNodesBeforeTranslation).toBe(0);
expect(unsafeTextNodes).toBe(0);
expect(firstTag).toBe(scenario.expectedFirstTag);
expect(visibleText(result.htmlAfterCommit)).toBe(scenario.expectedText);
});
}
});
}
describe('faithful compound pipeline', () => {
const inlineWrap = (text: string): ReactNode => createElement('span', null, text);
const contentsWrap = (text: string): ReactNode => createElement('flx-i18n', {style: {display: 'contents'}}, text);
it('defeats a bare text sibling', () => {
const result = commitUnderTranslation({
before: row(badge(false), 'Hello', tail),
after: row(badge(true), 'Hello', tail),
pipeline: chromeFaithfulCompoundPipeline,
});
expect(isTranslationDomCrash(result.error)).toBe(true);
});
it('defeats an inline sole-child wrapper', () => {
const result = commitUnderTranslation({
before: row(badge(false), inlineWrap('Hello'), tail),
after: row(badge(true), inlineWrap('Hello'), tail),
pipeline: chromeFaithfulCompoundPipeline,
});
expect(isTranslationDomCrash(result.error)).toBe(true);
});
it('defeats the display:contents wrapper the app ships', () => {
const result = commitUnderTranslation({
before: row(badge(false), contentsWrap('Hello'), tail),
after: row(badge(true), contentsWrap('Hello'), tail),
pipeline: chromeFaithfulCompoundPipeline,
});
expect(isTranslationDomCrash(result.error)).toBe(true);
});
it('defeats a pure element tree that holds no text node of its own', () => {
let reassembledChildren = false;
const pureElementRow = (on: boolean): ReactNode =>
row(badge(on), createElement('i', null, 'a'), createElement('em', null, 'y'));
const result = commitUnderTranslation({
before: pureElementRow(false),
after: pureElementRow(true),
pipeline: (host) => {
const before = Array.from(host.querySelector('div')?.childNodes ?? []);
chromeFaithfulCompoundPipeline(host);
const after = Array.from(host.querySelector('div')?.childNodes ?? []);
reassembledChildren = before.some((node, index) => node !== after[index]);
},
});
expect(reassembledChildren).toBe(true);
expect(result.unsafeTextNodesBeforeTranslation).toBe(0);
expect(isTranslationDomCrash(result.error)).toBe(true);
});
it('is survived only by translate="no"', () => {
const result = commitUnderTranslation({
before: createElement('div', {translate: 'no'}, badge(false), contentsWrap('Hello'), tail),
after: createElement('div', {translate: 'no'}, badge(true), contentsWrap('Hello'), tail),
pipeline: chromeFaithfulCompoundPipeline,
});
expect(result.htmlAfterTranslation).toBe(result.htmlBeforeTranslation);
expect(result.error).toBeNull();
expect(result.htmlAfterCommit).toContain('<b>!</b>');
expect(visibleText(result.htmlAfterCommit)).toBe('!Hellox');
});
});
describe('shapes that were never at risk', () => {
it('does not throw when only elements are siblings', () => {
const result = commitUnderTranslation({
before: row(badge(false), createElement('i', null, 'a')),
after: row(badge(true), createElement('i', null, 'a')),
pipeline: TranslatorPipelines.chromeLegacyFont,
});
expect(result.htmlAfterTranslation).not.toBe(result.htmlBeforeTranslation);
expect(result.error).toBeNull();
});
it('does not throw when a sibling is appended after the text', () => {
const result = commitUnderTranslation({
before: row('Hello', badge(false)),
after: row('Hello', badge(true)),
pipeline: TranslatorPipelines.chromeLegacyFont,
});
expect(result.htmlAfterTranslation).not.toBe(result.htmlBeforeTranslation);
expect(result.error).toBeNull();
});
});
@@ -1,343 +0,0 @@
// @vitest-environment happy-dom
// SPDX-License-Identifier: AGPL-3.0-or-later
import {commitUnderTranslation, isTranslationDomCrash} from '@app/features/i18n/testing/TranslationCommitHarness';
import {TranslatorPipelines} from '@app/features/i18n/testing/TranslatorSimulation';
import {createElement, type ReactNode} from 'react';
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
type GuardModule = typeof import('@app/features/i18n/utils/TranslationDomGuard');
type DeveloperOptionsModule = typeof import('@app/features/devtools/state/DeveloperOptions');
interface LoadedGuard {
guard: GuardModule;
developerOptions: DeveloperOptionsModule['default'];
}
let loaded: LoadedGuard | null = null;
let originalInsertBefore: typeof Node.prototype.insertBefore;
let originalRemoveChild: typeof Node.prototype.removeChild;
async function loadGuard(options: {electron?: boolean; disabled?: boolean} = {}): Promise<LoadedGuard> {
vi.resetModules();
const {Platform} = await import('@app/features/platform/types/Platform');
Platform.isElectron = options.electron === true;
const developerOptions = (await import('@app/features/devtools/state/DeveloperOptions')).default;
developerOptions.updateOption('disableTranslationDomGuard', options.disabled === true);
const guard = await import('@app/features/i18n/utils/TranslationDomGuard');
loaded = {guard, developerOptions};
return loaded;
}
function translatorDisplacedText(): {container: HTMLElement; text: Text} {
const container = document.createElement('div');
container.setAttribute('data-flx', 'guard-fixture');
const outer = document.createElement('font');
const inner = document.createElement('font');
const text = document.createTextNode('Hello');
inner.appendChild(text);
outer.appendChild(inner);
container.appendChild(outer);
document.body.appendChild(container);
return {container, text};
}
function detachedTranslatedText(): {container: HTMLElement; text: Text} {
const container = document.createElement('div');
const outer = document.createElement('font');
const inner = document.createElement('font');
const text = document.createTextNode('Hello');
inner.appendChild(text);
outer.appendChild(inner);
container.appendChild(outer);
document.body.appendChild(container);
document.documentElement.classList.add('translated-ltr');
outer.replaceChildren();
inner.replaceChildren();
return {container, text};
}
function unrelatedSubtreeText(): {container: HTMLElement; other: HTMLElement; text: Text} {
const container = document.createElement('div');
const other = document.createElement('div');
other.id = 'other';
const font = document.createElement('font');
const text = document.createTextNode('Hello');
font.appendChild(text);
other.appendChild(font);
document.body.append(container, other);
return {container, other, text};
}
function foreignParentText(): {container: HTMLElement; text: Text} {
const container = document.createElement('div');
const elsewhere = document.createElement('div');
const text = document.createTextNode('Hello');
elsewhere.appendChild(text);
document.body.append(container, elsewhere);
return {container, text};
}
const badge = (on: boolean): ReactNode => (on ? createElement('b', null, '!') : null);
const tail = createElement('i', null, 'x');
const unsafeRow = (on: boolean): ReactNode =>
createElement('div', null, badge(on), 'Hello', createElement('i', null, 'x'));
beforeEach(() => {
originalInsertBefore = Node.prototype.insertBefore;
originalRemoveChild = Node.prototype.removeChild;
});
afterEach(() => {
loaded?.guard.uninstallTranslationDomGuard();
loaded?.developerOptions.updateOption('disableTranslationDomGuard', false);
loaded = null;
Node.prototype.insertBefore = originalInsertBefore;
Node.prototype.removeChild = originalRemoveChild;
document.body.replaceChildren();
document.documentElement.className = '';
vi.restoreAllMocks();
});
describe('installation', () => {
it('patches both prototype methods and restores them by identity on uninstall', async () => {
const {guard} = await loadGuard();
guard.installTranslationDomGuard();
expect(Node.prototype.insertBefore).not.toBe(originalInsertBefore);
expect(Node.prototype.removeChild).not.toBe(originalRemoveChild);
expect(guard.getTranslationDomGuardStats().installed).toBe(true);
guard.installTranslationDomGuard();
expect(guard.uninstallTranslationDomGuard()).toBe(true);
expect(Node.prototype.insertBefore).toBe(originalInsertBefore);
expect(Node.prototype.removeChild).toBe(originalRemoveChild);
expect(guard.uninstallTranslationDomGuard()).toBe(false);
expect(guard.getTranslationDomGuardStats().installed).toBe(false);
});
it('never installs in Electron', async () => {
const {guard} = await loadGuard({electron: true});
guard.installTranslationDomGuard();
expect(Node.prototype.insertBefore).toBe(originalInsertBefore);
expect(guard.getTranslationDomGuardStats().installed).toBe(false);
});
it('never installs when the Electron native context is unavailable', async () => {
vi.spyOn(navigator, 'userAgent', 'get').mockReturnValue(
'Mozilla/5.0 (Macintosh) fluxer/1.0.0 Chrome/140.0.0.0 Electron/38.2.1 Safari/537.36',
);
const {guard} = await loadGuard({electron: false});
guard.installTranslationDomGuard();
expect(Node.prototype.insertBefore).toBe(originalInsertBefore);
expect(Node.prototype.removeChild).toBe(originalRemoveChild);
expect(guard.getTranslationDomGuardStats().installed).toBe(false);
});
it('never patches while the kill switch is already set', async () => {
const {guard} = await loadGuard({disabled: true});
guard.installTranslationDomGuard();
expect(Node.prototype.insertBefore).toBe(originalInsertBefore);
expect(guard.getTranslationDomGuardStats().installed).toBe(false);
});
it('uninstalls and reinstalls live when the kill switch is toggled', async () => {
const {guard, developerOptions} = await loadGuard();
guard.installTranslationDomGuard();
expect(guard.getTranslationDomGuardStats().installed).toBe(true);
developerOptions.updateOption('disableTranslationDomGuard', true);
expect(Node.prototype.insertBefore).toBe(originalInsertBefore);
expect(guard.getTranslationDomGuardStats().installed).toBe(false);
developerOptions.updateOption('disableTranslationDomGuard', false);
expect(Node.prototype.insertBefore).not.toBe(originalInsertBefore);
expect(guard.getTranslationDomGuardStats().installed).toBe(true);
});
it('exposes stats and a live uninstall on the debug object', async () => {
const {guard} = await loadGuard();
guard.installTranslationDomGuard();
const debug = window.__FLUXER_DEBUG__;
const getStats = debug?.getTranslationDomGuardStats as
| (() => ReturnType<GuardModule['getTranslationDomGuardStats']>)
| undefined;
expect(getStats?.().installed).toBe(true);
expect(debug?.uninstallTranslationDomGuard?.()).toBe(true);
expect(Node.prototype.insertBefore).toBe(originalInsertBefore);
});
});
describe('reporting', () => {
it('counts and logs every repaired insertBefore', async () => {
const {guard} = await loadGuard();
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
guard.installTranslationDomGuard();
const {container, text} = translatorDisplacedText();
expect(() => container.insertBefore(document.createElement('b'), text)).not.toThrow();
const stats = guard.getTranslationDomGuardStats();
expect(stats.insertBefore).toBe(1);
expect(stats.removeChild).toBe(0);
expect(stats.declined).toBe(0);
expect(stats.firstFireAt).not.toBeNull();
expect(stats.lastPath).toBe('guard-fixture');
const logged = consoleError.mock.calls.flat().join(' ');
expect(logged).toContain('TranslationDomGuard');
expect(logged).toContain('insertBefore');
});
it('counts and logs every repaired removeChild', async () => {
const {guard} = await loadGuard();
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
guard.installTranslationDomGuard();
const {container, text} = translatorDisplacedText();
expect(() => container.removeChild(text)).not.toThrow();
const stats = guard.getTranslationDomGuardStats();
expect(stats.removeChild).toBe(1);
expect(stats.insertBefore).toBe(0);
expect(text.parentNode).toBeNull();
expect(container.innerHTML).toBe('');
expect(consoleError.mock.calls.flat().join(' ')).toContain('removeChild');
});
it('keeps counting after it stops logging', async () => {
const {guard} = await loadGuard();
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
guard.installTranslationDomGuard();
for (let attempt = 0; attempt < 5; attempt += 1) {
const {container, text} = translatorDisplacedText();
container.insertBefore(document.createElement('b'), text);
}
expect(guard.getTranslationDomGuardStats().insertBefore).toBe(5);
expect(consoleError).toHaveBeenCalledTimes(3);
});
it('leaves calls on the original path alone', async () => {
const {guard} = await loadGuard();
guard.installTranslationDomGuard();
const parent = document.createElement('div');
const child = document.createElement('span');
parent.appendChild(child);
parent.insertBefore(document.createElement('b'), child);
parent.removeChild(child);
expect(parent.innerHTML).toBe('<b></b>');
const stats = guard.getTranslationDomGuardStats();
expect(stats.insertBefore).toBe(0);
expect(stats.removeChild).toBe(0);
expect(stats.declined).toBe(0);
});
it('declines to repair a displacement no translator caused', async () => {
const {guard} = await loadGuard();
guard.installTranslationDomGuard();
const {container, text} = foreignParentText();
expect(() => container.insertBefore(document.createElement('b'), text)).toThrow(
/Failed to execute 'insertBefore' on 'Node'/,
);
expect(() => container.removeChild(text)).toThrow(/Failed to execute 'removeChild' on 'Node'/);
const stats = guard.getTranslationDomGuardStats();
expect(stats.insertBefore).toBe(0);
expect(stats.removeChild).toBe(0);
expect(stats.declined).toBe(2);
});
});
describe('the guard behind the structural fix', () => {
it('turns the production crash into a reported fire', async () => {
const {guard} = await loadGuard();
vi.spyOn(console, 'error').mockImplementation(() => {});
const unguarded = commitUnderTranslation({
before: unsafeRow(false),
after: unsafeRow(true),
pipeline: TranslatorPipelines.chromeLegacyFont,
});
expect(isTranslationDomCrash(unguarded.error)).toBe(true);
guard.installTranslationDomGuard();
const guarded = commitUnderTranslation({
before: unsafeRow(false),
after: unsafeRow(true),
pipeline: TranslatorPipelines.chromeLegacyFont,
});
expect(guarded.error).toBeNull();
expect(guard.getTranslationDomGuardStats().insertBefore).toBeGreaterThan(0);
});
it('repairs the placement at the position the translator displaced', async () => {
const {guard} = await loadGuard();
vi.spyOn(console, 'error').mockImplementation(() => {});
guard.installTranslationDomGuard();
let firstTag: string | null = null;
const result = commitUnderTranslation({
before: unsafeRow(false),
after: unsafeRow(true),
pipeline: TranslatorPipelines.chromeLegacyFont,
inspect: (host) => {
firstTag = host.querySelector('div')?.firstElementChild?.tagName ?? null;
},
});
expect(result.error).toBeNull();
expect(firstTag).toBe('B');
expect(result.htmlAfterCommit.replace(/<[^>]+>/g, '')).toBe('!~Hello~x');
});
it('leaves no empty husk behind after a repaired deletion', async () => {
const {guard} = await loadGuard();
vi.spyOn(console, 'error').mockImplementation(() => {});
guard.installTranslationDomGuard();
let html = '';
let fontCount = -1;
let emptyFontCount = -1;
const result = commitUnderTranslation({
before: createElement('div', null, 'Hello', tail),
after: createElement('div', null, null, tail),
pipeline: TranslatorPipelines.chromeLegacyFont,
inspect: (host) => {
html = host.innerHTML;
const fonts = Array.from(host.querySelectorAll('font'));
fontCount = fonts.length;
emptyFontCount = fonts.filter((font) => font.childNodes.length === 0).length;
},
});
expect(result.error).toBeNull();
expect(guard.getTranslationDomGuardStats().removeChild).toBeGreaterThan(0);
expect(html.replace(/<[^>]+>/g, '')).toBe('~x');
expect(html).not.toContain('Hello');
expect(emptyFontCount).toBe(0);
expect(fontCount).toBe(2);
});
it('no-ops a removeChild of a node the translator already detached', async () => {
const {guard} = await loadGuard();
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
guard.installTranslationDomGuard();
const {container, text} = detachedTranslatedText();
expect(text.parentNode).toBeNull();
expect(container.removeChild(text)).toBe(text);
expect(text.parentNode).toBeNull();
expect(guard.getTranslationDomGuardStats().removeChild).toBe(1);
expect(consoleError.mock.calls.flat().join(' ')).toContain('removeChild');
});
it('does not unwind husks outside the container the removal was addressed to', async () => {
const {guard} = await loadGuard();
vi.spyOn(console, 'error').mockImplementation(() => {});
guard.installTranslationDomGuard();
const {container, other, text} = unrelatedSubtreeText();
expect(container.removeChild(text)).toBe(text);
expect(text.parentNode).toBeNull();
expect(other.parentNode).toBe(document.body);
expect(document.getElementById('other')).toBe(other);
expect(other.innerHTML).toBe('<font></font>');
expect(guard.getTranslationDomGuardStats().removeChild).toBe(1);
});
});