From cdf02d54dadc7b625c54e6561b6044a57d69a900 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 8 Sep 2023 10:22:13 -0700 Subject: [PATCH] Share test code between dom and webgl --- .../test/WebglRenderer.test.ts | 1128 +---------------- addons/xterm-addon-webgl/test/tsconfig.json | 3 +- bin/test_playwright.js | 2 +- test/playwright/Renderer.test.ts | 1118 +--------------- test/playwright/SharedRendererTests.ts | 1122 ++++++++++++++++ 5 files changed, 1141 insertions(+), 2232 deletions(-) create mode 100644 test/playwright/SharedRendererTests.ts diff --git a/addons/xterm-addon-webgl/test/WebglRenderer.test.ts b/addons/xterm-addon-webgl/test/WebglRenderer.test.ts index 9c0418b4..5fc5d300 100644 --- a/addons/xterm-addon-webgl/test/WebglRenderer.test.ts +++ b/addons/xterm-addon-webgl/test/WebglRenderer.test.ts @@ -3,16 +3,17 @@ * @license MIT */ -import { assert } from 'chai'; -import test, { LocatorScreenshotOptions } from '@playwright/test'; -import { ITheme } from 'xterm'; -import { ITestContext, MaybeAsync, createTestContext, openTerminal, pollFor } from '../../../out-test/playwright/TestUtils'; -import { IImage32, decodePng } from '@lunapaint/png-codec'; +import test from '@playwright/test'; +import { strictEqual } from 'assert'; +import { injectSharedRendererTests } from '../../../out-test/playwright/SharedRendererTests'; +import { ITestContext, createTestContext, openTerminal } from '../../../out-test/playwright/TestUtils'; let ctx: ITestContext; +const ctxWrapper: { value: ITestContext } = { value: undefined } as any; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx); + ctxWrapper.value = ctx; await ctx.page.evaluate(` window.addon = new WebglAddon(true); window.term.loadAddon(window.addon); @@ -20,22 +21,11 @@ test.beforeAll(async ({ browser }) => { }); test.afterAll(async () => await ctx.page.close()); -test.describe.only('WebGL Renderer Integration Tests', async () => { - test.beforeEach(async () => { - await ctx.proxy.reset(); - ctx.page.evaluate(` - window.term.options.minimumContrastRatio = 1; - window.term.options.allowTransparency = false; - `); - // TODO: Introduce frame caching - // Clear the cached screenshot before each test - frameDetails = undefined; - }); - +test.describe('WebGL Renderer Integration Tests', async () => { test('dispose removes renderer canvases', async function(): Promise { - assert.equal(await ctx.page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 2); + strictEqual(await ctx.page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 2); await ctx.page.evaluate(`addon.dispose()`); - assert.equal(await ctx.page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 0); + strictEqual(await ctx.page.evaluate(`document.querySelectorAll('.xterm canvas').length`), 0); // Re-create webgl addon to avoid side effects impacting other tests await ctx.page.evaluate(` window.addon = new WebglAddon(true); @@ -43,1103 +33,5 @@ test.describe.only('WebGL Renderer Integration Tests', async () => { `); }); - test.describe('colors', () => { - test('foreground 0-15', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[30m█\x1b[31m█\x1b[32m█\x1b[33m█\x1b[34m█\x1b[35m█\x1b[36m█\x1b[37m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 0-7 drawBoldTextInBrightColors', async () => { - const theme: ITheme = { - brightBlack: '#010203', - brightRed: '#040506', - brightGreen: '#070809', - brightYellow: '#0a0b0c', - brightBlue: '#0d0e0f', - brightMagenta: '#101112', - brightCyan: '#131415', - brightWhite: '#161718' - }; - await ctx.page.evaluate(` - window.term.options.theme = ${JSON.stringify(theme)}; - window.term.options.drawBoldTextInBrightColors = true; - `); - await ctx.proxy.write(`\x1b[1;30m█\x1b[1;31m█\x1b[1;32m█\x1b[1;33m█\x1b[1;34m█\x1b[1;35m█\x1b[1;36m█\x1b[1;37m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('background 0-15', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[40m \x1b[41m \x1b[42m \x1b[43m \x1b[44m \x1b[45m \x1b[46m \x1b[47m `); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 0-15 inverse', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[7;30m \x1b[7;31m \x1b[7;32m \x1b[7;33m \x1b[7;34m \x1b[7;35m \x1b[7;36m \x1b[7;37m `); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('background 0-15 inverse', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[7;40m█\x1b[7;41m█\x1b[7;42m█\x1b[7;43m█\x1b[7;44m█\x1b[7;45m█\x1b[7;46m█\x1b[7;47m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 0-15 inivisible', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[8;30m \x1b[8;31m \x1b[8;32m \x1b[8;33m \x1b[8;34m \x1b[8;35m \x1b[8;36m \x1b[8;37m `); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [0, 0, 0, 255]); - }); - - test('background 0-15 inivisible', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[8;40m█\x1b[8;41m█\x1b[8;42m█\x1b[8;43m█\x1b[8;44m█\x1b[8;45m█\x1b[8;46m█\x1b[8;47m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 0-15 bright', async () => { - const theme: ITheme = { - brightBlack: '#010203', - brightRed: '#040506', - brightGreen: '#070809', - brightYellow: '#0a0b0c', - brightBlue: '#0d0e0f', - brightMagenta: '#101112', - brightCyan: '#131415', - brightWhite: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[90m█\x1b[91m█\x1b[92m█\x1b[93m█\x1b[94m█\x1b[95m█\x1b[96m█\x1b[97m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('background 0-15 bright', async () => { - const theme: ITheme = { - brightBlack: '#010203', - brightRed: '#040506', - brightGreen: '#070809', - brightYellow: '#0a0b0c', - brightBlue: '#0d0e0f', - brightMagenta: '#101112', - brightCyan: '#131415', - brightWhite: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[100m \x1b[101m \x1b[102m \x1b[103m \x1b[104m \x1b[105m \x1b[106m \x1b[107m `); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 16-255', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[38;5;${16 + y * 16 + x}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('background 16-255', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[48;5;${16 + y * 16 + x}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('foreground 16-255 inverse', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[7;38;5;${16 + y * 16 + x}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('background 16-255 inverse', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[7;48;5;${16 + y * 16 + x}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('foreground 16-255 invisible', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[8;38;5;${16 + y * 16 + x}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, 0, 255]); - } - } - }); - - test('background 16-255 invisible', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[8;48;5;${16 + y * 16 + x}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('foreground 16-255 dim', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[2;38;5;${16 + y * 16 + x}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - // It's difficult to assert the exact color due to rounding, just ensure the color differs - // to the regular color - await pollFor(ctx.page, async () => { - const c = await getCellColor(x + 1, y + 1); - return ( - (c[0] === 0 || c[0] !== r) && - (c[1] === 0 || c[1] !== g) && - (c[2] === 0 || c[2] !== b) - ); - }, true); - } - } - }); - - test('background 16-255 dim', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[2;48;5;${16 + y * 16 + x}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('foreground true color red', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[38;2;${i};0;0m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); - } - } - }); - - test('background true color red', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[48;2;${i};0;0m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); - } - } - }); - - test('foreground true color green', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[38;2;0;${i};0m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); - } - } - }); - - test('background true color green', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[48;2;0;${i};0m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); - } - } - }); - - test('foreground true color blue', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[38;2;0;0;${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); - } - } - }); - - test('background true color blue', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[48;2;0;0;${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); - } - } - }); - - test('foreground true color grey', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[38;2;${i};${i};${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test('background true color grey', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[48;2;${i};${i};${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test('foreground true color red inverse', async function(): Promise { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;38;2;${i};0;0m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); - } - } - }); - - test('background true color red inverse', async function(): Promise { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;48;2;${i};0;0m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); - } - } - }); - - test('foreground true color green inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;38;2;0;${i};0m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); - } - } - }); - - test('background true color green inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;48;2;0;${i};0m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); - } - } - }); - - test('foreground true color blue inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;38;2;0;0;${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); - } - } - }); - - test('background true color blue inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;48;2;0;0;${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); - } - } - }); - - test('foreground true color grey inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;38;2;${i};${i};${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test('background true color grey inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;48;2;${i};${i};${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test('foreground true color grey invisible', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[8;38;2;${i};${i};${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, 0, 255]); - } - } - }); - - test('background true color grey invisible', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[8;48;2;${i};${i};${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - }); - - test.describe('minimumContrastRatio', async () => { - test('should adjust 0-15 colors on black background', async () => { - const theme: ITheme = { - black: '#2e3436', - red: '#cc0000', - green: '#4e9a06', - yellow: '#c4a000', - blue: '#3465a4', - magenta: '#75507b', - cyan: '#06989a', - white: '#d3d7cf', - brightBlack: '#555753', - brightRed: '#ef2929', - brightGreen: '#8ae234', - brightYellow: '#fce94f', - brightBlue: '#729fcf', - brightMagenta: '#ad7fa8', - brightCyan: '#34e2e2', - brightWhite: '#eeeeec' - }; - await ctx.page.evaluate(` - window.term.options.theme = ${JSON.stringify(theme)}; - window.term.options.minimumContrastRatio = 1; - `); - // Block elements ignore minimum contrast ratio so a different char is used here - await ctx.proxy.write( - `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + - `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` - ); - // Validate before minimumContrastRatio is applied - await pollFor(ctx.page, () => getCellColor(1, 1), [0x2e, 0x34, 0x36, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [0xcc, 0x00, 0x00, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0x4e, 0x9a, 0x06, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [0xc4, 0xa0, 0x00, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [0x34, 0x65, 0xa4, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [0x75, 0x50, 0x7b, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [0x06, 0x98, 0x9a, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [0xd3, 0xd7, 0xcf, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [0x55, 0x57, 0x53, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [0xef, 0x29, 0x29, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [0x8a, 0xe2, 0x34, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [0xfc, 0xe9, 0x4f, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [0x72, 0x9f, 0xcf, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [0xad, 0x7f, 0xa8, 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [0x34, 0xe2, 0xe2, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [0xee, 0xee, 0xec, 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail - await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); - frameDetails = undefined; - await pollFor(ctx.page, () => getCellColor(1, 1), [176, 180, 180, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [238, 158, 158, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [152, 198, 110, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [208, 179, 49, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [161, 183, 215, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [191, 174, 194, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [110, 197, 198, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [211, 215, 207, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [183, 185, 183, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [249, 156, 156, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [138, 226, 52, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [252, 233, 79, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [154, 186, 221, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [203, 173, 199, 255]); - // Unchanged - await pollFor(ctx.page, () => getCellColor(7, 2), [0x34, 0xe2, 0xe2, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [0xee, 0xee, 0xec, 255]); - }); - - test('should adjust 0-15 colors on white background', async () => { - const theme: ITheme = { - background: '#ffffff', - black: '#2e3436', - red: '#cc0000', - green: '#4e9a06', - yellow: '#c4a000', - blue: '#3465a4', - magenta: '#75507b', - cyan: '#06989a', - white: '#d3d7cf', - brightBlack: '#555753', - brightRed: '#ef2929', - brightGreen: '#8ae234', - brightYellow: '#fce94f', - brightBlue: '#729fcf', - brightMagenta: '#ad7fa8', - brightCyan: '#34e2e2', - brightWhite: '#eeeeec' - }; - await ctx.page.evaluate(` - window.term.options.theme = ${JSON.stringify(theme)}; - window.term.options.minimumContrastRatio = 1; - `); - // Block elements ignore minimum contrast ratio so a different char is used here - await ctx.proxy.write( - `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + - `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` - ); - // Validate before minimumContrastRatio is applied - await pollFor(ctx.page, () => getCellColor(1, 1), [0x2e, 0x34, 0x36, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [0xcc, 0x00, 0x00, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0x4e, 0x9a, 0x06, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [0xc4, 0xa0, 0x00, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [0x34, 0x65, 0xa4, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [0x75, 0x50, 0x7b, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [0x06, 0x98, 0x9a, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [0xd3, 0xd7, 0xcf, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [0x55, 0x57, 0x53, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [0xef, 0x29, 0x29, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [0x8a, 0xe2, 0x34, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [0xfc, 0xe9, 0x4f, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [0x72, 0x9f, 0xcf, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [0xad, 0x7f, 0xa8, 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [0x34, 0xe2, 0xe2, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [0xee, 0xee, 0xec, 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail - await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); - frameDetails = undefined; - await pollFor(ctx.page, () => getCellColor(1, 1), [46, 52, 54, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [132, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [36, 72, 0, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [72, 59, 0, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [32, 64, 106, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [75, 51, 80, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [0, 71, 72, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [64, 64, 63, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [61, 63, 59, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [125, 19, 19, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [40, 67, 13, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [67, 63, 19, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [45, 65, 87, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [81, 57, 78, 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [13, 67, 67, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [64, 64, 64, 255]); - }); - - test('should enforce half the contrast for dim cells', async () => { - // TODO: This test fails on webkit - if (ctx.browser.browserType().name() === 'webkit') { - test.skip(); - return; - } - const theme: ITheme = { - background: '#ffffff', - black: '#2e3436', - red: '#cc0000', - green: '#4e9a06', - yellow: '#c4a000', - blue: '#3465a4', - magenta: '#75507b', - cyan: '#06989a', - white: '#d3d7cf', - brightBlack: '#555753', - brightRed: '#ef2929', - brightGreen: '#8ae234', - brightYellow: '#fce94f', - brightBlue: '#729fcf', - brightMagenta: '#ad7fa8', - brightCyan: '#34e2e2', - brightWhite: '#eeeeec' - }; - await ctx.page.evaluate(` - window.term.options.theme = ${JSON.stringify(theme)}; - window.term.options.minimumContrastRatio = 1; - `); - // Block elements ignore minimum contrast ratio so a different char is used here - await ctx.proxy.write( - '\x1b[2m' + - `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + - `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` - ); - // Validate before minimumContrastRatio is applied - await pollFor(ctx.page, () => getCellColor(1, 1), [Math.floor((255 + 0x2e) / 2), Math.floor((255 + 0x34) / 2), Math.floor((255 + 0x36) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [Math.floor((255 + 0xcc) / 2), Math.floor((255 + 0x00) / 2), Math.floor((255 + 0x00) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [Math.floor((255 + 0x4e) / 2), Math.floor((255 + 0x9a) / 2), Math.floor((255 + 0x06) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [Math.floor((255 + 0xc4) / 2), Math.floor((255 + 0xa0) / 2), Math.floor((255 + 0x00) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0x65) / 2), Math.floor((255 + 0xa4) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [Math.floor((255 + 0x75) / 2), Math.floor((255 + 0x50) / 2), Math.floor((255 + 0x7b) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [Math.floor((255 + 0x06) / 2), Math.floor((255 + 0x98) / 2), Math.floor((255 + 0x9a) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [Math.floor((255 + 0xd3) / 2), Math.floor((255 + 0xd7) / 2), Math.floor((255 + 0xcf) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [Math.floor((255 + 0x55) / 2), Math.floor((255 + 0x57) / 2), Math.floor((255 + 0x53) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [Math.floor((255 + 0xef) / 2), Math.floor((255 + 0x29) / 2), Math.floor((255 + 0x29) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [Math.floor((255 + 0x8a) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0x34) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [Math.floor((255 + 0xfc) / 2), Math.floor((255 + 0xe9) / 2), Math.floor((255 + 0x4f) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [Math.floor((255 + 0x72) / 2), Math.floor((255 + 0x9f) / 2), Math.floor((255 + 0xcf) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [Math.floor((255 + 0xad) / 2), Math.floor((255 + 0x7f) / 2), Math.floor((255 + 0xa8) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0xe2) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xec) / 2), 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail - await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); - frameDetails = undefined; - await pollFor(ctx.page, () => getCellColor(1, 1), [150, 153, 154, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [229, 127, 127, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [63, 124, 4, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [127, 104, 0, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [153, 178, 209, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [186, 167, 189, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [4, 122, 124, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [110, 112, 108, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [170, 171, 169, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [215, 36, 36, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [72, 117, 25, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [117, 109, 36, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [72, 103, 135, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [125, 91, 121, 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [25, 117, 117, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [111, 111, 110, 255]); - }); - }); - - test.describe('selectionBackground', async () => { - test('should resolve the inverse foreground color based on the original background color, not the selection', async () => { - const theme: ITheme = { - foreground: '#FF0000', - background: '#00FF00', - selectionBackground: '#0000FF' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(` █\x1b[7m█\x1b[0m`); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 255, 0, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [255, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0, 255, 0, 255]); - await ctx.page.evaluate(`window.term.selectAll()`); - frameDetails = undefined; - // Selection only cell needs to be first to ensure renderer has kicked in - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 255, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [255, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0, 255, 0, 255]); - }); - }); - - test.describe('allowTransparency', async () => { - test.beforeEach(() => ctx.page.evaluate(`term.options.allowTransparency = true`)); - - test('transparent background inverse', async () => { - const theme: ITheme = { - background: '#ff000080' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - const data = `\x1b[7m█\x1b[0m`; - await ctx.proxy.write(data); - // Inverse background should be opaque - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); - }); - }); - - test.describe('selectionForeground', () => { - test('transparent background inverse', async () => { - const theme: ITheme = { - selectionForeground: '#ff0000' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - const data = `\x1b[7m█\x1b[0m`; - await ctx.proxy.write(data); - await ctx.page.evaluate(`window.term.selectAll()`); - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); - }); - }); - - test.describe('decoration color overrides', async () => { - test('foregroundColor', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - foregroundColor: '#ff0000', - backgroundColor: '#0000ff' - }); - `); - const data = `█`; - await ctx.proxy.write(data); - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); - }); - test('foregroundColor should ignore inverse', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - foregroundColor: '#ff0000', - backgroundColor: '#0000ff' - }); - `); - const data = `\x1b[7m█\x1b[0m`; - await ctx.proxy.write(data); - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); - }); - test('foregroundColor should ignore inverse (only fg on decoration)', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - width: 2, - foregroundColor: '#ff0000' - }); - `); - const data = `\x1b[7m█ \x1b[0m`; - await ctx.proxy.write(data); - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); // inverse foreground of '█' should be decoration fg override - await pollFor(ctx.page, () => getCellColor(2, 1), [255, 255, 255, 255]); // inverse background of ' ' should be default foreground - }); - test('backgroundColor', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - foregroundColor: '#ff0000', - backgroundColor: '#0000ff' - }); - `); - const data = ` `; - await ctx.proxy.write(data); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 255, 255]); - }); - test('backgroundColor should ignore inverse', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - foregroundColor: '#ff0000', - backgroundColor: '#0000ff' - }); - `); - const data = `\x1b[7m \x1b[0m`; - await ctx.proxy.write(data); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 255, 255]); - }); - test('backgroundColor should ignore inverse (only bg on decoration)', async () => { - const data = `\x1b[7m█ \x1b[0m`; - await ctx.proxy.write(data); - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - width: 2, - backgroundColor: '#0000ff' - }); - `); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 0, 255]); // inverse foreground of '█' should be default - await pollFor(ctx.page, () => getCellColor(2, 1), [0, 0, 255, 255]); // inverse background of ' ' should be decoration bg override - }); - }); + injectSharedRendererTests(ctxWrapper); }); - -/** - * Gets the color of the pixel in the center of a cell. - * @param col The 1-based column index to get the color for. - * @param row The 1-based row index to get the color for. - */ -function getCellColor(col: number, row: number): MaybeAsync<[red: number, green: number, blue: number, alpha: number]> { - if (!frameDetails) { - return getFrameDetails().then(frameDetails => getCellColorInner(frameDetails, col, row)); - } - return getCellColorInner(frameDetails, col, row); -} - -let frameDetails: { cols: number, rows: number, decoded: IImage32 } | undefined = undefined; -async function getFrameDetails(): Promise<{ cols: number, rows: number, decoded: IImage32 }> { - const screenshotOptions: LocatorScreenshotOptions | undefined = process.env.DEBUG ? { path: 'out-test/playwright/screenshot.png' } : undefined; - const buffer = await ctx.page.locator('#terminal-container .xterm-screen').screenshot(screenshotOptions); - frameDetails = { - cols: await ctx.proxy.cols, - rows: await ctx.proxy.rows, - decoded: (await decodePng(buffer, { force32: true })).image - }; - return frameDetails; -} - -function getCellColorInner(frameDetails: { cols: number, rows: number, decoded: IImage32 }, col: number, row: number): [red: number, green: number, blue: number, alpha: number] { - const cellSize = { - width: frameDetails.decoded.width / frameDetails.cols, - height: frameDetails.decoded.height / frameDetails.rows - }; - const x = Math.floor((col - 1/* 1- to 0-based index */ + 0.5/* middle of cell */) * cellSize.width); - const y = Math.floor((row - 1/* 1- to 0-based index */ + 0.5/* middle of cell */) * cellSize.height); - const i = (y * frameDetails.decoded.width + x) * 4/* 4 channels per pixel */; - return Array.from(frameDetails.decoded.data.slice(i, i + 4)) as [number, number, number, number]; -} - -const COLORS_16_TO_255 = [ - '#000000', '#00005f', '#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f', '#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f', '#008787', '#0087af', - '#0087d7', '#0087ff', '#00af00', '#00af5f', '#00af87', '#00afaf', '#00afd7', '#00afff', '#00d700', '#00d75f', '#00d787', '#00d7af', '#00d7d7', '#00d7ff', '#00ff00', '#00ff5f', - '#00ff87', '#00ffaf', '#00ffd7', '#00ffff', '#5f0000', '#5f005f', '#5f0087', '#5f00af', '#5f00d7', '#5f00ff', '#5f5f00', '#5f5f5f', '#5f5f87', '#5f5faf', '#5f5fd7', '#5f5fff', - '#5f8700', '#5f875f', '#5f8787', '#5f87af', '#5f87d7', '#5f87ff', '#5faf00', '#5faf5f', '#5faf87', '#5fafaf', '#5fafd7', '#5fafff', '#5fd700', '#5fd75f', '#5fd787', '#5fd7af', - '#5fd7d7', '#5fd7ff', '#5fff00', '#5fff5f', '#5fff87', '#5fffaf', '#5fffd7', '#5fffff', '#870000', '#87005f', '#870087', '#8700af', '#8700d7', '#8700ff', '#875f00', '#875f5f', - '#875f87', '#875faf', '#875fd7', '#875fff', '#878700', '#87875f', '#878787', '#8787af', '#8787d7', '#8787ff', '#87af00', '#87af5f', '#87af87', '#87afaf', '#87afd7', '#87afff', - '#87d700', '#87d75f', '#87d787', '#87d7af', '#87d7d7', '#87d7ff', '#87ff00', '#87ff5f', '#87ff87', '#87ffaf', '#87ffd7', '#87ffff', '#af0000', '#af005f', '#af0087', '#af00af', - '#af00d7', '#af00ff', '#af5f00', '#af5f5f', '#af5f87', '#af5faf', '#af5fd7', '#af5fff', '#af8700', '#af875f', '#af8787', '#af87af', '#af87d7', '#af87ff', '#afaf00', '#afaf5f', - '#afaf87', '#afafaf', '#afafd7', '#afafff', '#afd700', '#afd75f', '#afd787', '#afd7af', '#afd7d7', '#afd7ff', '#afff00', '#afff5f', '#afff87', '#afffaf', '#afffd7', '#afffff', - '#d70000', '#d7005f', '#d70087', '#d700af', '#d700d7', '#d700ff', '#d75f00', '#d75f5f', '#d75f87', '#d75faf', '#d75fd7', '#d75fff', '#d78700', '#d7875f', '#d78787', '#d787af', - '#d787d7', '#d787ff', '#d7af00', '#d7af5f', '#d7af87', '#d7afaf', '#d7afd7', '#d7afff', '#d7d700', '#d7d75f', '#d7d787', '#d7d7af', '#d7d7d7', '#d7d7ff', '#d7ff00', '#d7ff5f', - '#d7ff87', '#d7ffaf', '#d7ffd7', '#d7ffff', '#ff0000', '#ff005f', '#ff0087', '#ff00af', '#ff00d7', '#ff00ff', '#ff5f00', '#ff5f5f', '#ff5f87', '#ff5faf', '#ff5fd7', '#ff5fff', - '#ff8700', '#ff875f', '#ff8787', '#ff87af', '#ff87d7', '#ff87ff', '#ffaf00', '#ffaf5f', '#ffaf87', '#ffafaf', '#ffafd7', '#ffafff', '#ffd700', '#ffd75f', '#ffd787', '#ffd7af', - '#ffd7d7', '#ffd7ff', '#ffff00', '#ffff5f', '#ffff87', '#ffffaf', '#ffffd7', '#ffffff', '#080808', '#121212', '#1c1c1c', '#262626', '#303030', '#3a3a3a', '#444444', '#4e4e4e', - '#585858', '#626262', '#6c6c6c', '#767676', '#808080', '#8a8a8a', '#949494', '#9e9e9e', '#a8a8a8', '#b2b2b2', '#bcbcbc', '#c6c6c6', '#d0d0d0', '#dadada', '#e4e4e4', '#eeeeee' -]; diff --git a/addons/xterm-addon-webgl/test/tsconfig.json b/addons/xterm-addon-webgl/test/tsconfig.json index 8a4135a6..9523e64b 100644 --- a/addons/xterm-addon-webgl/test/tsconfig.json +++ b/addons/xterm-addon-webgl/test/tsconfig.json @@ -22,7 +22,8 @@ "types": [ "../../../node_modules/@types/node", "../../../node_modules/@lunapaint/png-codec", - "../../../out-test/playwright/TestUtils" + "../../../out-test/playwright/TestUtils", + "../../../out-test/playwright/SharedRendererTests" ] }, "include": [ diff --git a/bin/test_playwright.js b/bin/test_playwright.js index 809f29a8..bc4e7b02 100644 --- a/bin/test_playwright.js +++ b/bin/test_playwright.js @@ -9,7 +9,7 @@ const cp = require('child_process'); const path = require('path'); const configs = [ - // { name: 'core', path: 'out-test/playwright/playwright.config.js' }, + { name: 'core', path: 'out-test/playwright/playwright.config.js' }, { name: 'xterm-addon-webgl', path: 'addons/xterm-addon-webgl/out-test/playwright.config.js' } ]; diff --git a/test/playwright/Renderer.test.ts b/test/playwright/Renderer.test.ts index f407dc5d..8050001f 100644 --- a/test/playwright/Renderer.test.ts +++ b/test/playwright/Renderer.test.ts @@ -3,1126 +3,20 @@ * @license MIT */ -import { IImage32, decodePng } from '@lunapaint/png-codec'; -import { LocatorScreenshotOptions, test } from '@playwright/test'; -import { ITheme } from 'xterm'; -import { createTestContext, ITestContext, MaybeAsync, openTerminal, pollFor } from './TestUtils'; +import { test } from '@playwright/test'; +import { ITestContext, createTestContext, openTerminal } from './TestUtils'; +import { injectSharedRendererTests } from './SharedRendererTests'; let ctx: ITestContext; +const ctxWrapper: { value: ITestContext } = { value: undefined } as any; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); + ctxWrapper.value = ctx; await openTerminal(ctx); }); test.afterAll(async () => await ctx.page.close()); test.describe('DOM Renderer Integration Tests', async () => { - test.beforeEach(async () => { - await ctx.proxy.reset(); - ctx.page.evaluate(` - window.term.options.minimumContrastRatio = 1; - window.term.options.allowTransparency = false; - `); - // Clear the cached screenshot before each test - frameDetails = undefined; - }); - - test.describe('colors', () => { - test('foreground 0-15', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[30m█\x1b[31m█\x1b[32m█\x1b[33m█\x1b[34m█\x1b[35m█\x1b[36m█\x1b[37m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - }); - - test('foreground 0-7 drawBoldTextInBrightColors', async () => { - const theme: ITheme = { - brightBlack: '#010203', - brightRed: '#040506', - brightGreen: '#070809', - brightYellow: '#0a0b0c', - brightBlue: '#0d0e0f', - brightMagenta: '#101112', - brightCyan: '#131415', - brightWhite: '#161718' - }; - await ctx.page.evaluate(` - window.term.options.theme = ${JSON.stringify(theme)}; - window.term.options.drawBoldTextInBrightColors = true; - `); - await ctx.proxy.write(`\x1b[1;30m█\x1b[1;31m█\x1b[1;32m█\x1b[1;33m█\x1b[1;34m█\x1b[1;35m█\x1b[1;36m█\x1b[1;37m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('background 0-15', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[40m \x1b[41m \x1b[42m \x1b[43m \x1b[44m \x1b[45m \x1b[46m \x1b[47m `); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 0-15 inverse', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[7;30m \x1b[7;31m \x1b[7;32m \x1b[7;33m \x1b[7;34m \x1b[7;35m \x1b[7;36m \x1b[7;37m `); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('background 0-15 inverse', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[7;40m█\x1b[7;41m█\x1b[7;42m█\x1b[7;43m█\x1b[7;44m█\x1b[7;45m█\x1b[7;46m█\x1b[7;47m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 0-15 inivisible', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[8;30m \x1b[8;31m \x1b[8;32m \x1b[8;33m \x1b[8;34m \x1b[8;35m \x1b[8;36m \x1b[8;37m `); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [0, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [0, 0, 0, 255]); - }); - - test('background 0-15 inivisible', async () => { - const theme: ITheme = { - black: '#010203', - red: '#040506', - green: '#070809', - yellow: '#0a0b0c', - blue: '#0d0e0f', - magenta: '#101112', - cyan: '#131415', - white: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[8;40m█\x1b[8;41m█\x1b[8;42m█\x1b[8;43m█\x1b[8;44m█\x1b[8;45m█\x1b[8;46m█\x1b[8;47m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 0-15 bright', async () => { - const theme: ITheme = { - brightBlack: '#010203', - brightRed: '#040506', - brightGreen: '#070809', - brightYellow: '#0a0b0c', - brightBlue: '#0d0e0f', - brightMagenta: '#101112', - brightCyan: '#131415', - brightWhite: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[90m█\x1b[91m█\x1b[92m█\x1b[93m█\x1b[94m█\x1b[95m█\x1b[96m█\x1b[97m█`); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('background 0-15 bright', async () => { - const theme: ITheme = { - brightBlack: '#010203', - brightRed: '#040506', - brightGreen: '#070809', - brightYellow: '#0a0b0c', - brightBlue: '#0d0e0f', - brightMagenta: '#101112', - brightCyan: '#131415', - brightWhite: '#161718' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write(`\x1b[100m \x1b[101m \x1b[102m \x1b[103m \x1b[104m \x1b[105m \x1b[106m \x1b[107m `); - await pollFor(ctx.page, () => getCellColor(1, 1), [1, 2, 3, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [4, 5, 6, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [7, 8, 9, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [10, 11, 12, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [13, 14, 15, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [16, 17, 18, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [19, 20, 21, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [22, 23, 24, 255]); - }); - - test('foreground 16-255', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[38;5;${16 + y * 16 + x}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('background 16-255', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[48;5;${16 + y * 16 + x}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('foreground 16-255 inverse', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[7;38;5;${16 + y * 16 + x}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('background 16-255 inverse', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[7;48;5;${16 + y * 16 + x}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('foreground 16-255 invisible', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[8;38;5;${16 + y * 16 + x}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, 0, 255]); - } - } - }); - - test('background 16-255 invisible', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[8;48;5;${16 + y * 16 + x}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('foreground 16-255 dim', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[2;38;5;${16 + y * 16 + x}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - // It's difficult to assert the exact color due to rounding, just ensure the color differs - // to the regular color - await pollFor(ctx.page, async () => { - const c = await getCellColor(x + 1, y + 1); - return ( - (c[0] === 0 || c[0] !== r) && - (c[1] === 0 || c[1] !== g) && - (c[2] === 0 || c[2] !== b) - ); - }, true); - } - } - }); - - test('background 16-255 dim', async () => { - let data = ''; - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - data += `\x1b[2;48;5;${16 + y * 16 + x}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 240 / 16; y++) { - for (let x = 0; x < 16; x++) { - const cssColor = COLORS_16_TO_255[y * 16 + x]; - const r = parseInt(cssColor.slice(1, 3), 16); - const g = parseInt(cssColor.slice(3, 5), 16); - const b = parseInt(cssColor.slice(5, 7), 16); - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [r, g, b, 255]); - } - } - }); - - test('foreground true color red', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[38;2;${i};0;0m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); - } - } - }); - - test('background true color red', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[48;2;${i};0;0m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); - } - } - }); - - test('foreground true color green', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[38;2;0;${i};0m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); - } - } - }); - - test('background true color green', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[48;2;0;${i};0m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); - } - } - }); - - test('foreground true color blue', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[38;2;0;0;${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); - } - } - }); - - test('background true color blue', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[48;2;0;0;${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); - } - } - }); - - test('foreground true color grey', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[38;2;${i};${i};${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test('background true color grey', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[48;2;${i};${i};${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test('foreground true color red inverse', async function(): Promise { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;38;2;${i};0;0m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); - } - } - }); - - test('background true color red inverse', async function(): Promise { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;48;2;${i};0;0m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, 0, 0, 255]); - } - } - }); - - test('foreground true color green inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;38;2;0;${i};0m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); - } - } - }); - - test('background true color green inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;48;2;0;${i};0m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, i, 0, 255]); - } - } - }); - - test('foreground true color blue inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;38;2;0;0;${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); - } - } - }); - - test('background true color blue inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;48;2;0;0;${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, i, 255]); - } - } - }); - - test('foreground true color grey inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;38;2;${i};${i};${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test('background true color grey inverse', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[7;48;2;${i};${i};${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test('foreground true color grey invisible', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[8;38;2;${i};${i};${i}m \x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [0, 0, 0, 255]); - } - } - }); - - test('background true color grey invisible', async () => { - let data = ''; - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - data += `\x1b[8;48;2;${i};${i};${i}m█\x1b[0m`; - } - data += '\r\n'; - } - await ctx.proxy.write(data); - for (let y = 0; y < 16; y++) { - for (let x = 0; x < 16; x++) { - const i = y * 16 + x; - await pollFor(ctx.page, () => getCellColor(x + 1, y + 1), [i, i, i, 255]); - } - } - }); - - test.describe('minimumContrastRatio', async () => { - test('should adjust 0-15 colors on black background', async () => { - const theme: ITheme = { - black: '#2e3436', - red: '#cc0000', - green: '#4e9a06', - yellow: '#c4a000', - blue: '#3465a4', - magenta: '#75507b', - cyan: '#06989a', - white: '#d3d7cf', - brightBlack: '#555753', - brightRed: '#ef2929', - brightGreen: '#8ae234', - brightYellow: '#fce94f', - brightBlue: '#729fcf', - brightMagenta: '#ad7fa8', - brightCyan: '#34e2e2', - brightWhite: '#eeeeec' - }; - await ctx.page.evaluate(` - window.term.options.theme = ${JSON.stringify(theme)}; - window.term.options.minimumContrastRatio = 1; - `); - // Block elements ignore minimum contrast ratio so a different char is used here - await ctx.proxy.write( - `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + - `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` - ); - // Validate before minimumContrastRatio is applied - await pollFor(ctx.page, () => getCellColor(1, 1), [0x2e, 0x34, 0x36, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [0xcc, 0x00, 0x00, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0x4e, 0x9a, 0x06, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [0xc4, 0xa0, 0x00, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [0x34, 0x65, 0xa4, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [0x75, 0x50, 0x7b, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [0x06, 0x98, 0x9a, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [0xd3, 0xd7, 0xcf, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [0x55, 0x57, 0x53, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [0xef, 0x29, 0x29, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [0x8a, 0xe2, 0x34, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [0xfc, 0xe9, 0x4f, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [0x72, 0x9f, 0xcf, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [0xad, 0x7f, 0xa8, 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [0x34, 0xe2, 0xe2, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [0xee, 0xee, 0xec, 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail - await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); - frameDetails = undefined; - await pollFor(ctx.page, () => getCellColor(1, 1), [176, 180, 180, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [238, 158, 158, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [152, 198, 110, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [208, 179, 49, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [161, 183, 215, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [191, 174, 194, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [110, 197, 198, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [211, 215, 207, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [183, 185, 183, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [249, 156, 156, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [138, 226, 52, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [252, 233, 79, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [154, 186, 221, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [203, 173, 199, 255]); - // Unchanged - await pollFor(ctx.page, () => getCellColor(7, 2), [0x34, 0xe2, 0xe2, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [0xee, 0xee, 0xec, 255]); - }); - - test('should adjust 0-15 colors on white background', async () => { - const theme: ITheme = { - background: '#ffffff', - black: '#2e3436', - red: '#cc0000', - green: '#4e9a06', - yellow: '#c4a000', - blue: '#3465a4', - magenta: '#75507b', - cyan: '#06989a', - white: '#d3d7cf', - brightBlack: '#555753', - brightRed: '#ef2929', - brightGreen: '#8ae234', - brightYellow: '#fce94f', - brightBlue: '#729fcf', - brightMagenta: '#ad7fa8', - brightCyan: '#34e2e2', - brightWhite: '#eeeeec' - }; - await ctx.page.evaluate(` - window.term.options.theme = ${JSON.stringify(theme)}; - window.term.options.minimumContrastRatio = 1; - `); - // Block elements ignore minimum contrast ratio so a different char is used here - await ctx.proxy.write( - `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + - `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` - ); - // Validate before minimumContrastRatio is applied - await pollFor(ctx.page, () => getCellColor(1, 1), [0x2e, 0x34, 0x36, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [0xcc, 0x00, 0x00, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0x4e, 0x9a, 0x06, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [0xc4, 0xa0, 0x00, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [0x34, 0x65, 0xa4, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [0x75, 0x50, 0x7b, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [0x06, 0x98, 0x9a, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [0xd3, 0xd7, 0xcf, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [0x55, 0x57, 0x53, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [0xef, 0x29, 0x29, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [0x8a, 0xe2, 0x34, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [0xfc, 0xe9, 0x4f, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [0x72, 0x9f, 0xcf, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [0xad, 0x7f, 0xa8, 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [0x34, 0xe2, 0xe2, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [0xee, 0xee, 0xec, 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail - await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); - frameDetails = undefined; - await pollFor(ctx.page, () => getCellColor(1, 1), [46, 52, 54, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [132, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [36, 72, 0, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [72, 59, 0, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [32, 64, 106, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [75, 51, 80, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [0, 71, 72, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [64, 64, 63, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [61, 63, 59, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [125, 19, 19, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [40, 67, 13, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [67, 63, 19, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [45, 65, 87, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [81, 57, 78, 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [13, 67, 67, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [64, 64, 64, 255]); - }); - - test('should enforce half the contrast for dim cells', async () => { - // TODO: This test fails on webkit - if (ctx.browser.browserType().name() === 'webkit') { - test.skip(); - return; - } - const theme: ITheme = { - background: '#ffffff', - black: '#2e3436', - red: '#cc0000', - green: '#4e9a06', - yellow: '#c4a000', - blue: '#3465a4', - magenta: '#75507b', - cyan: '#06989a', - white: '#d3d7cf', - brightBlack: '#555753', - brightRed: '#ef2929', - brightGreen: '#8ae234', - brightYellow: '#fce94f', - brightBlue: '#729fcf', - brightMagenta: '#ad7fa8', - brightCyan: '#34e2e2', - brightWhite: '#eeeeec' - }; - await ctx.page.evaluate(` - window.term.options.theme = ${JSON.stringify(theme)}; - window.term.options.minimumContrastRatio = 1; - `); - // Block elements ignore minimum contrast ratio so a different char is used here - await ctx.proxy.write( - '\x1b[2m' + - `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + - `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` - ); - // Validate before minimumContrastRatio is applied - await pollFor(ctx.page, () => getCellColor(1, 1), [Math.floor((255 + 0x2e) / 2), Math.floor((255 + 0x34) / 2), Math.floor((255 + 0x36) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [Math.floor((255 + 0xcc) / 2), Math.floor((255 + 0x00) / 2), Math.floor((255 + 0x00) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [Math.floor((255 + 0x4e) / 2), Math.floor((255 + 0x9a) / 2), Math.floor((255 + 0x06) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [Math.floor((255 + 0xc4) / 2), Math.floor((255 + 0xa0) / 2), Math.floor((255 + 0x00) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0x65) / 2), Math.floor((255 + 0xa4) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [Math.floor((255 + 0x75) / 2), Math.floor((255 + 0x50) / 2), Math.floor((255 + 0x7b) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [Math.floor((255 + 0x06) / 2), Math.floor((255 + 0x98) / 2), Math.floor((255 + 0x9a) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [Math.floor((255 + 0xd3) / 2), Math.floor((255 + 0xd7) / 2), Math.floor((255 + 0xcf) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [Math.floor((255 + 0x55) / 2), Math.floor((255 + 0x57) / 2), Math.floor((255 + 0x53) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [Math.floor((255 + 0xef) / 2), Math.floor((255 + 0x29) / 2), Math.floor((255 + 0x29) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [Math.floor((255 + 0x8a) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0x34) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [Math.floor((255 + 0xfc) / 2), Math.floor((255 + 0xe9) / 2), Math.floor((255 + 0x4f) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [Math.floor((255 + 0x72) / 2), Math.floor((255 + 0x9f) / 2), Math.floor((255 + 0xcf) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [Math.floor((255 + 0xad) / 2), Math.floor((255 + 0x7f) / 2), Math.floor((255 + 0xa8) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0xe2) / 2), 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xec) / 2), 255]); - // Setting and check for minimum contrast values, note that these are not - // exact to the contrast ratio, if the increase luminance algorithm - // changes then these will probably fail - await ctx.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); - frameDetails = undefined; - await pollFor(ctx.page, () => getCellColor(1, 1), [150, 153, 154, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [229, 127, 127, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [63, 124, 4, 255]); - await pollFor(ctx.page, () => getCellColor(4, 1), [127, 104, 0, 255]); - await pollFor(ctx.page, () => getCellColor(5, 1), [153, 178, 209, 255]); - await pollFor(ctx.page, () => getCellColor(6, 1), [186, 167, 189, 255]); - await pollFor(ctx.page, () => getCellColor(7, 1), [4, 122, 124, 255]); - await pollFor(ctx.page, () => getCellColor(8, 1), [110, 112, 108, 255]); - await pollFor(ctx.page, () => getCellColor(1, 2), [170, 171, 169, 255]); - await pollFor(ctx.page, () => getCellColor(2, 2), [215, 36, 36, 255]); - await pollFor(ctx.page, () => getCellColor(3, 2), [72, 117, 25, 255]); - await pollFor(ctx.page, () => getCellColor(4, 2), [117, 109, 36, 255]); - await pollFor(ctx.page, () => getCellColor(5, 2), [72, 103, 135, 255]); - await pollFor(ctx.page, () => getCellColor(6, 2), [125, 91, 121, 255]); - await pollFor(ctx.page, () => getCellColor(7, 2), [25, 117, 117, 255]); - await pollFor(ctx.page, () => getCellColor(8, 2), [111, 111, 110, 255]); - }); - }); - - test.describe('selectionBackground', async () => { - test('should resolve the inverse foreground color based on the original background color, not the selection', async () => { - const theme: ITheme = { - foreground: '#FF0000', - background: '#00FF00', - selectionBackground: '#0000FF' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - await ctx.proxy.write( ` █\x1b[7m█\x1b[0m`); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 255, 0, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [255, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0, 255, 0, 255]); - await ctx.page.evaluate(`window.term.selectAll()`); - frameDetails = undefined; - // Selection only cell needs to be first to ensure renderer has kicked in - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 255, 255]); - await pollFor(ctx.page, () => getCellColor(2, 1), [255, 0, 0, 255]); - await pollFor(ctx.page, () => getCellColor(3, 1), [0, 255, 0, 255]); - }); - }); - - test.describe('allowTransparency', async () => { - test.beforeEach(() => ctx.page.evaluate(`term.options.allowTransparency = true`)); - - test('transparent background inverse', async () => { - const theme: ITheme = { - background: '#ff000080' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - const data = `\x1b[7m█\x1b[0m`; - await ctx.proxy.write( data); - // Inverse background should be opaque - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); - }); - }); - - test.describe('selectionForeground', () => { - test('transparent background inverse', async () => { - const theme: ITheme = { - selectionForeground: '#ff0000' - }; - await ctx.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); - const data = `\x1b[7m█\x1b[0m`; - await ctx.proxy.write( data); - await ctx.page.evaluate(`window.term.selectAll()`); - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); - }); - }); - - test.describe('decoration color overrides', async () => { - test('foregroundColor', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - foregroundColor: '#ff0000', - backgroundColor: '#0000ff' - }); - `); - const data = `█`; - await ctx.proxy.write( data); - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); - }); - test('foregroundColor should ignore inverse', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - foregroundColor: '#ff0000', - backgroundColor: '#0000ff' - }); - `); - const data = `\x1b[7m█\x1b[0m`; - await ctx.proxy.write( data); - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); - }); - test('foregroundColor should ignore inverse (only fg on decoration)', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - width: 2, - foregroundColor: '#ff0000' - }); - `); - const data = `\x1b[7m█ \x1b[0m`; - await ctx.proxy.write( data); - await pollFor(ctx.page, () => getCellColor(1, 1), [255, 0, 0, 255]); // inverse foreground of '█' should be decoration fg override - await pollFor(ctx.page, () => getCellColor(2, 1), [255, 255, 255, 255]); // inverse background of ' ' should be default foreground - }); - test('backgroundColor', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - foregroundColor: '#ff0000', - backgroundColor: '#0000ff' - }); - `); - const data = ` `; - await ctx.proxy.write( data); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 255, 255]); - }); - test('backgroundColor should ignore inverse', async () => { - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - foregroundColor: '#ff0000', - backgroundColor: '#0000ff' - }); - `); - const data = `\x1b[7m \x1b[0m`; - await ctx.proxy.write( data); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 255, 255]); - }); - test('backgroundColor should ignore inverse (only bg on decoration)', async () => { - const data = `\x1b[7m█ \x1b[0m`; - await ctx.proxy.write( data); - await ctx.page.evaluate(` - const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); - window.term.registerDecoration({ - marker, - width: 2, - backgroundColor: '#0000ff' - }); - `); - await pollFor(ctx.page, () => getCellColor(1, 1), [0, 0, 0, 255]); // inverse foreground of '█' should be default - await pollFor(ctx.page, () => getCellColor(2, 1), [0, 0, 255, 255]); // inverse background of ' ' should be decoration bg override - }); - }); + injectSharedRendererTests(ctxWrapper); }); -/** - * Gets the color of the pixel in the center of a cell. - * @param col The 1-based column index to get the color for. - * @param row The 1-based row index to get the color for. - */ -function getCellColor(col: number, row: number): MaybeAsync<[red: number, green: number, blue: number, alpha: number]> { - if (!frameDetails) { - return getFrameDetails().then(frameDetails => getCellColorInner(frameDetails, col, row)); - } - return getCellColorInner(frameDetails, col, row); -} - -let frameDetails: { cols: number, rows: number, decoded: IImage32 } | undefined = undefined; -async function getFrameDetails(): Promise<{ cols: number, rows: number, decoded: IImage32 }> { - const screenshotOptions: LocatorScreenshotOptions | undefined = process.env.DEBUG ? { path: 'out-test/playwright/screenshot.png' } : undefined; - const buffer = await ctx.page.locator('#terminal-container .xterm-rows').screenshot(screenshotOptions); - frameDetails = { - cols: await ctx.proxy.cols, - rows: await ctx.proxy.rows, - decoded: (await decodePng(buffer, { force32: true })).image - }; - return frameDetails; -} - -function getCellColorInner(frameDetails: { cols: number, rows: number, decoded: IImage32 }, col: number, row: number): [red: number, green: number, blue: number, alpha: number] { - const cellSize = { - width: frameDetails.decoded.width / frameDetails.cols, - height: frameDetails.decoded.height / frameDetails.rows - }; - const x = Math.floor((col - 1/* 1- to 0-based index */ + 0.5/* middle of cell */) * cellSize.width); - const y = Math.floor((row - 1/* 1- to 0-based index */ + 0.5/* middle of cell */) * cellSize.height); - const i = (y * frameDetails.decoded.width + x) * 4/* 4 channels per pixel */; - return Array.from(frameDetails.decoded.data.slice(i, i + 4)) as [number, number, number, number]; -} - -const COLORS_16_TO_255 = [ - '#000000', '#00005f', '#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f', '#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f', '#008787', '#0087af', - '#0087d7', '#0087ff', '#00af00', '#00af5f', '#00af87', '#00afaf', '#00afd7', '#00afff', '#00d700', '#00d75f', '#00d787', '#00d7af', '#00d7d7', '#00d7ff', '#00ff00', '#00ff5f', - '#00ff87', '#00ffaf', '#00ffd7', '#00ffff', '#5f0000', '#5f005f', '#5f0087', '#5f00af', '#5f00d7', '#5f00ff', '#5f5f00', '#5f5f5f', '#5f5f87', '#5f5faf', '#5f5fd7', '#5f5fff', - '#5f8700', '#5f875f', '#5f8787', '#5f87af', '#5f87d7', '#5f87ff', '#5faf00', '#5faf5f', '#5faf87', '#5fafaf', '#5fafd7', '#5fafff', '#5fd700', '#5fd75f', '#5fd787', '#5fd7af', - '#5fd7d7', '#5fd7ff', '#5fff00', '#5fff5f', '#5fff87', '#5fffaf', '#5fffd7', '#5fffff', '#870000', '#87005f', '#870087', '#8700af', '#8700d7', '#8700ff', '#875f00', '#875f5f', - '#875f87', '#875faf', '#875fd7', '#875fff', '#878700', '#87875f', '#878787', '#8787af', '#8787d7', '#8787ff', '#87af00', '#87af5f', '#87af87', '#87afaf', '#87afd7', '#87afff', - '#87d700', '#87d75f', '#87d787', '#87d7af', '#87d7d7', '#87d7ff', '#87ff00', '#87ff5f', '#87ff87', '#87ffaf', '#87ffd7', '#87ffff', '#af0000', '#af005f', '#af0087', '#af00af', - '#af00d7', '#af00ff', '#af5f00', '#af5f5f', '#af5f87', '#af5faf', '#af5fd7', '#af5fff', '#af8700', '#af875f', '#af8787', '#af87af', '#af87d7', '#af87ff', '#afaf00', '#afaf5f', - '#afaf87', '#afafaf', '#afafd7', '#afafff', '#afd700', '#afd75f', '#afd787', '#afd7af', '#afd7d7', '#afd7ff', '#afff00', '#afff5f', '#afff87', '#afffaf', '#afffd7', '#afffff', - '#d70000', '#d7005f', '#d70087', '#d700af', '#d700d7', '#d700ff', '#d75f00', '#d75f5f', '#d75f87', '#d75faf', '#d75fd7', '#d75fff', '#d78700', '#d7875f', '#d78787', '#d787af', - '#d787d7', '#d787ff', '#d7af00', '#d7af5f', '#d7af87', '#d7afaf', '#d7afd7', '#d7afff', '#d7d700', '#d7d75f', '#d7d787', '#d7d7af', '#d7d7d7', '#d7d7ff', '#d7ff00', '#d7ff5f', - '#d7ff87', '#d7ffaf', '#d7ffd7', '#d7ffff', '#ff0000', '#ff005f', '#ff0087', '#ff00af', '#ff00d7', '#ff00ff', '#ff5f00', '#ff5f5f', '#ff5f87', '#ff5faf', '#ff5fd7', '#ff5fff', - '#ff8700', '#ff875f', '#ff8787', '#ff87af', '#ff87d7', '#ff87ff', '#ffaf00', '#ffaf5f', '#ffaf87', '#ffafaf', '#ffafd7', '#ffafff', '#ffd700', '#ffd75f', '#ffd787', '#ffd7af', - '#ffd7d7', '#ffd7ff', '#ffff00', '#ffff5f', '#ffff87', '#ffffaf', '#ffffd7', '#ffffff', '#080808', '#121212', '#1c1c1c', '#262626', '#303030', '#3a3a3a', '#444444', '#4e4e4e', - '#585858', '#626262', '#6c6c6c', '#767676', '#808080', '#8a8a8a', '#949494', '#9e9e9e', '#a8a8a8', '#b2b2b2', '#bcbcbc', '#c6c6c6', '#d0d0d0', '#dadada', '#e4e4e4', '#eeeeee' -]; diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts new file mode 100644 index 00000000..2120925f --- /dev/null +++ b/test/playwright/SharedRendererTests.ts @@ -0,0 +1,1122 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IImage32, decodePng } from '@lunapaint/png-codec'; +import { LocatorScreenshotOptions, test } from '@playwright/test'; +import { ITheme } from 'xterm'; +import { ITestContext, MaybeAsync, pollFor } from './TestUtils'; + +export function injectSharedRendererTests(ctx: { value: ITestContext }): void { + test.beforeEach(async () => { + await ctx.value.proxy.reset(); + ctx.value.page.evaluate(` + window.term.options.minimumContrastRatio = 1; + window.term.options.allowTransparency = false; + `); + // Clear the cached screenshot before each test + frameDetails = undefined; + }); + + test.describe('colors', () => { + test('foreground 0-15', async () => { + const theme: ITheme = { + black: '#010203', + red: '#040506', + green: '#070809', + yellow: '#0a0b0c', + blue: '#0d0e0f', + magenta: '#101112', + cyan: '#131415', + white: '#161718' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write(`\x1b[30m█\x1b[31m█\x1b[32m█\x1b[33m█\x1b[34m█\x1b[35m█\x1b[36m█\x1b[37m█`); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [1, 2, 3, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [4, 5, 6, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [7, 8, 9, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [10, 11, 12, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [13, 14, 15, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [16, 17, 18, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [19, 20, 21, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [22, 23, 24, 255]); + }); + }); + + test('foreground 0-7 drawBoldTextInBrightColors', async () => { + const theme: ITheme = { + brightBlack: '#010203', + brightRed: '#040506', + brightGreen: '#070809', + brightYellow: '#0a0b0c', + brightBlue: '#0d0e0f', + brightMagenta: '#101112', + brightCyan: '#131415', + brightWhite: '#161718' + }; + await ctx.value.page.evaluate(` + window.term.options.theme = ${JSON.stringify(theme)}; + window.term.options.drawBoldTextInBrightColors = true; + `); + await ctx.value.proxy.write(`\x1b[1;30m█\x1b[1;31m█\x1b[1;32m█\x1b[1;33m█\x1b[1;34m█\x1b[1;35m█\x1b[1;36m█\x1b[1;37m█`); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [1, 2, 3, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [4, 5, 6, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [7, 8, 9, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [10, 11, 12, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [13, 14, 15, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [16, 17, 18, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [19, 20, 21, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [22, 23, 24, 255]); + }); + + test('background 0-15', async () => { + const theme: ITheme = { + black: '#010203', + red: '#040506', + green: '#070809', + yellow: '#0a0b0c', + blue: '#0d0e0f', + magenta: '#101112', + cyan: '#131415', + white: '#161718' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write(`\x1b[40m \x1b[41m \x1b[42m \x1b[43m \x1b[44m \x1b[45m \x1b[46m \x1b[47m `); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [1, 2, 3, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [4, 5, 6, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [7, 8, 9, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [10, 11, 12, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [13, 14, 15, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [16, 17, 18, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [19, 20, 21, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [22, 23, 24, 255]); + }); + + test('foreground 0-15 inverse', async () => { + const theme: ITheme = { + black: '#010203', + red: '#040506', + green: '#070809', + yellow: '#0a0b0c', + blue: '#0d0e0f', + magenta: '#101112', + cyan: '#131415', + white: '#161718' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write(`\x1b[7;30m \x1b[7;31m \x1b[7;32m \x1b[7;33m \x1b[7;34m \x1b[7;35m \x1b[7;36m \x1b[7;37m `); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [1, 2, 3, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [4, 5, 6, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [7, 8, 9, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [10, 11, 12, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [13, 14, 15, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [16, 17, 18, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [19, 20, 21, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [22, 23, 24, 255]); + }); + + test('background 0-15 inverse', async () => { + const theme: ITheme = { + black: '#010203', + red: '#040506', + green: '#070809', + yellow: '#0a0b0c', + blue: '#0d0e0f', + magenta: '#101112', + cyan: '#131415', + white: '#161718' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write(`\x1b[7;40m█\x1b[7;41m█\x1b[7;42m█\x1b[7;43m█\x1b[7;44m█\x1b[7;45m█\x1b[7;46m█\x1b[7;47m█`); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [1, 2, 3, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [4, 5, 6, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [7, 8, 9, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [10, 11, 12, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [13, 14, 15, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [16, 17, 18, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [19, 20, 21, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [22, 23, 24, 255]); + }); + + test('foreground 0-15 inivisible', async () => { + const theme: ITheme = { + black: '#010203', + red: '#040506', + green: '#070809', + yellow: '#0a0b0c', + blue: '#0d0e0f', + magenta: '#101112', + cyan: '#131415', + white: '#161718' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write(`\x1b[8;30m \x1b[8;31m \x1b[8;32m \x1b[8;33m \x1b[8;34m \x1b[8;35m \x1b[8;36m \x1b[8;37m `); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [0, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [0, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [0, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [0, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [0, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [0, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [0, 0, 0, 255]); + }); + + test('background 0-15 inivisible', async () => { + const theme: ITheme = { + black: '#010203', + red: '#040506', + green: '#070809', + yellow: '#0a0b0c', + blue: '#0d0e0f', + magenta: '#101112', + cyan: '#131415', + white: '#161718' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write(`\x1b[8;40m█\x1b[8;41m█\x1b[8;42m█\x1b[8;43m█\x1b[8;44m█\x1b[8;45m█\x1b[8;46m█\x1b[8;47m█`); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [1, 2, 3, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [4, 5, 6, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [7, 8, 9, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [10, 11, 12, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [13, 14, 15, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [16, 17, 18, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [19, 20, 21, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [22, 23, 24, 255]); + }); + + test('foreground 0-15 bright', async () => { + const theme: ITheme = { + brightBlack: '#010203', + brightRed: '#040506', + brightGreen: '#070809', + brightYellow: '#0a0b0c', + brightBlue: '#0d0e0f', + brightMagenta: '#101112', + brightCyan: '#131415', + brightWhite: '#161718' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write(`\x1b[90m█\x1b[91m█\x1b[92m█\x1b[93m█\x1b[94m█\x1b[95m█\x1b[96m█\x1b[97m█`); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [1, 2, 3, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [4, 5, 6, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [7, 8, 9, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [10, 11, 12, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [13, 14, 15, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [16, 17, 18, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [19, 20, 21, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [22, 23, 24, 255]); + }); + + test('background 0-15 bright', async () => { + const theme: ITheme = { + brightBlack: '#010203', + brightRed: '#040506', + brightGreen: '#070809', + brightYellow: '#0a0b0c', + brightBlue: '#0d0e0f', + brightMagenta: '#101112', + brightCyan: '#131415', + brightWhite: '#161718' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write(`\x1b[100m \x1b[101m \x1b[102m \x1b[103m \x1b[104m \x1b[105m \x1b[106m \x1b[107m `); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [1, 2, 3, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [4, 5, 6, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [7, 8, 9, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [10, 11, 12, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [13, 14, 15, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [16, 17, 18, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [19, 20, 21, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [22, 23, 24, 255]); + }); + + test('foreground 16-255', async () => { + let data = ''; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\x1b[38;5;${16 + y * 16 + x}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.slice(1, 3), 16); + const g = parseInt(cssColor.slice(3, 5), 16); + const b = parseInt(cssColor.slice(5, 7), 16); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [r, g, b, 255]); + } + } + }); + + test('background 16-255', async () => { + let data = ''; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\x1b[48;5;${16 + y * 16 + x}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.slice(1, 3), 16); + const g = parseInt(cssColor.slice(3, 5), 16); + const b = parseInt(cssColor.slice(5, 7), 16); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [r, g, b, 255]); + } + } + }); + + test('foreground 16-255 inverse', async () => { + let data = ''; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\x1b[7;38;5;${16 + y * 16 + x}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.slice(1, 3), 16); + const g = parseInt(cssColor.slice(3, 5), 16); + const b = parseInt(cssColor.slice(5, 7), 16); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [r, g, b, 255]); + } + } + }); + + test('background 16-255 inverse', async () => { + let data = ''; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\x1b[7;48;5;${16 + y * 16 + x}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.slice(1, 3), 16); + const g = parseInt(cssColor.slice(3, 5), 16); + const b = parseInt(cssColor.slice(5, 7), 16); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [r, g, b, 255]); + } + } + }); + + test('foreground 16-255 invisible', async () => { + let data = ''; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\x1b[8;38;5;${16 + y * 16 + x}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, 0, 0, 255]); + } + } + }); + + test('background 16-255 invisible', async () => { + let data = ''; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\x1b[8;48;5;${16 + y * 16 + x}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.slice(1, 3), 16); + const g = parseInt(cssColor.slice(3, 5), 16); + const b = parseInt(cssColor.slice(5, 7), 16); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [r, g, b, 255]); + } + } + }); + + test('foreground 16-255 dim', async () => { + let data = ''; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\x1b[2;38;5;${16 + y * 16 + x}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.slice(1, 3), 16); + const g = parseInt(cssColor.slice(3, 5), 16); + const b = parseInt(cssColor.slice(5, 7), 16); + // It's difficult to assert the exact color due to rounding, just ensure the color differs + // to the regular color + await pollFor(ctx.value.page, async () => { + const c = await getCellColor(ctx.value, x + 1, y + 1); + return ( + (c[0] === 0 || c[0] !== r) && + (c[1] === 0 || c[1] !== g) && + (c[2] === 0 || c[2] !== b) + ); + }, true); + } + } + }); + + test('background 16-255 dim', async () => { + let data = ''; + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + data += `\x1b[2;48;5;${16 + y * 16 + x}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 240 / 16; y++) { + for (let x = 0; x < 16; x++) { + const cssColor = COLORS_16_TO_255[y * 16 + x]; + const r = parseInt(cssColor.slice(1, 3), 16); + const g = parseInt(cssColor.slice(3, 5), 16); + const b = parseInt(cssColor.slice(5, 7), 16); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [r, g, b, 255]); + } + } + }); + + test('foreground true color red', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[38;2;${i};0;0m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, 0, 0, 255]); + } + } + }); + + test('background true color red', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[48;2;${i};0;0m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, 0, 0, 255]); + } + } + }); + + test('foreground true color green', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[38;2;0;${i};0m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, i, 0, 255]); + } + } + }); + + test('background true color green', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[48;2;0;${i};0m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, i, 0, 255]); + } + } + }); + + test('foreground true color blue', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[38;2;0;0;${i}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, 0, i, 255]); + } + } + }); + + test('background true color blue', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[48;2;0;0;${i}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, 0, i, 255]); + } + } + }); + + test('foreground true color grey', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[38;2;${i};${i};${i}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, i, i, 255]); + } + } + }); + + test('background true color grey', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[48;2;${i};${i};${i}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, i, i, 255]); + } + } + }); + + test('foreground true color red inverse', async function(): Promise { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[7;38;2;${i};0;0m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, 0, 0, 255]); + } + } + }); + + test('background true color red inverse', async function(): Promise { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[7;48;2;${i};0;0m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, 0, 0, 255]); + } + } + }); + + test('foreground true color green inverse', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[7;38;2;0;${i};0m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, i, 0, 255]); + } + } + }); + + test('background true color green inverse', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[7;48;2;0;${i};0m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, i, 0, 255]); + } + } + }); + + test('foreground true color blue inverse', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[7;38;2;0;0;${i}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, 0, i, 255]); + } + } + }); + + test('background true color blue inverse', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[7;48;2;0;0;${i}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, 0, i, 255]); + } + } + }); + + test('foreground true color grey inverse', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[7;38;2;${i};${i};${i}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, i, i, 255]); + } + } + }); + + test('background true color grey inverse', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[7;48;2;${i};${i};${i}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, i, i, 255]); + } + } + }); + + test('foreground true color grey invisible', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[8;38;2;${i};${i};${i}m \x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [0, 0, 0, 255]); + } + } + }); + + test('background true color grey invisible', async () => { + let data = ''; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + data += `\x1b[8;48;2;${i};${i};${i}m█\x1b[0m`; + } + data += '\r\n'; + } + await ctx.value.proxy.write(data); + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + const i = y * 16 + x; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, x + 1, y + 1), [i, i, i, 255]); + } + } + }); + + test.describe('minimumContrastRatio', async () => { + test('should adjust 0-15 colors on black background', async () => { + const theme: ITheme = { + black: '#2e3436', + red: '#cc0000', + green: '#4e9a06', + yellow: '#c4a000', + blue: '#3465a4', + magenta: '#75507b', + cyan: '#06989a', + white: '#d3d7cf', + brightBlack: '#555753', + brightRed: '#ef2929', + brightGreen: '#8ae234', + brightYellow: '#fce94f', + brightBlue: '#729fcf', + brightMagenta: '#ad7fa8', + brightCyan: '#34e2e2', + brightWhite: '#eeeeec' + }; + await ctx.value.page.evaluate(` + window.term.options.theme = ${JSON.stringify(theme)}; + window.term.options.minimumContrastRatio = 1; + `); + // Block elements ignore minimum contrast ratio so a different char is used here + await ctx.value.proxy.write( + `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + + `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` + ); + // Validate before minimumContrastRatio is applied + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0x2e, 0x34, 0x36, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [0xcc, 0x00, 0x00, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [0x4e, 0x9a, 0x06, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [0xc4, 0xa0, 0x00, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [0x34, 0x65, 0xa4, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [0x75, 0x50, 0x7b, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [0x06, 0x98, 0x9a, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [0xd3, 0xd7, 0xcf, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [0x55, 0x57, 0x53, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 2), [0xef, 0x29, 0x29, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 2), [0x8a, 0xe2, 0x34, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 2), [0xfc, 0xe9, 0x4f, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 2), [0x72, 0x9f, 0xcf, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail + await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); + frameDetails = undefined; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [176, 180, 180, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [238, 158, 158, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [152, 198, 110, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [208, 179, 49, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [161, 183, 215, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [191, 174, 194, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [110, 197, 198, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [211, 215, 207, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [183, 185, 183, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 2), [249, 156, 156, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 2), [138, 226, 52, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 2), [252, 233, 79, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 2), [154, 186, 221, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [203, 173, 199, 255]); + // Unchanged + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); + }); + + test('should adjust 0-15 colors on white background', async () => { + const theme: ITheme = { + background: '#ffffff', + black: '#2e3436', + red: '#cc0000', + green: '#4e9a06', + yellow: '#c4a000', + blue: '#3465a4', + magenta: '#75507b', + cyan: '#06989a', + white: '#d3d7cf', + brightBlack: '#555753', + brightRed: '#ef2929', + brightGreen: '#8ae234', + brightYellow: '#fce94f', + brightBlue: '#729fcf', + brightMagenta: '#ad7fa8', + brightCyan: '#34e2e2', + brightWhite: '#eeeeec' + }; + await ctx.value.page.evaluate(` + window.term.options.theme = ${JSON.stringify(theme)}; + window.term.options.minimumContrastRatio = 1; + `); + // Block elements ignore minimum contrast ratio so a different char is used here + await ctx.value.proxy.write( + `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + + `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` + ); + // Validate before minimumContrastRatio is applied + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0x2e, 0x34, 0x36, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [0xcc, 0x00, 0x00, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [0x4e, 0x9a, 0x06, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [0xc4, 0xa0, 0x00, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [0x34, 0x65, 0xa4, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [0x75, 0x50, 0x7b, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [0x06, 0x98, 0x9a, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [0xd3, 0xd7, 0xcf, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [0x55, 0x57, 0x53, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 2), [0xef, 0x29, 0x29, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 2), [0x8a, 0xe2, 0x34, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 2), [0xfc, 0xe9, 0x4f, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 2), [0x72, 0x9f, 0xcf, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [0xad, 0x7f, 0xa8, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [0x34, 0xe2, 0xe2, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [0xee, 0xee, 0xec, 255]); + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail + await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); + frameDetails = undefined; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [46, 52, 54, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [132, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [36, 72, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [72, 59, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [32, 64, 106, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [75, 51, 80, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [0, 71, 72, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [64, 64, 63, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [61, 63, 59, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 2), [125, 19, 19, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 2), [40, 67, 13, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 2), [67, 63, 19, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 2), [45, 65, 87, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [81, 57, 78, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [13, 67, 67, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [64, 64, 64, 255]); + }); + + test('should enforce half the contrast for dim cells', async () => { + // TODO: This test fails on webkit + if (ctx.value.browser.browserType().name() === 'webkit') { + test.skip(); + return; + } + const theme: ITheme = { + background: '#ffffff', + black: '#2e3436', + red: '#cc0000', + green: '#4e9a06', + yellow: '#c4a000', + blue: '#3465a4', + magenta: '#75507b', + cyan: '#06989a', + white: '#d3d7cf', + brightBlack: '#555753', + brightRed: '#ef2929', + brightGreen: '#8ae234', + brightYellow: '#fce94f', + brightBlue: '#729fcf', + brightMagenta: '#ad7fa8', + brightCyan: '#34e2e2', + brightWhite: '#eeeeec' + }; + await ctx.value.page.evaluate(` + window.term.options.theme = ${JSON.stringify(theme)}; + window.term.options.minimumContrastRatio = 1; + `); + // Block elements ignore minimum contrast ratio so a different char is used here + await ctx.value.proxy.write( + '\x1b[2m' + + `\x1b[30m■\x1b[31m■\x1b[32m■\x1b[33m■\x1b[34m■\x1b[35m■\x1b[36m■\x1b[37m■\r\n` + + `\x1b[90m■\x1b[91m■\x1b[92m■\x1b[93m■\x1b[94m■\x1b[95m■\x1b[96m■\x1b[97m■` + ); + // Validate before minimumContrastRatio is applied + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [Math.floor((255 + 0x2e) / 2), Math.floor((255 + 0x34) / 2), Math.floor((255 + 0x36) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [Math.floor((255 + 0xcc) / 2), Math.floor((255 + 0x00) / 2), Math.floor((255 + 0x00) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [Math.floor((255 + 0x4e) / 2), Math.floor((255 + 0x9a) / 2), Math.floor((255 + 0x06) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [Math.floor((255 + 0xc4) / 2), Math.floor((255 + 0xa0) / 2), Math.floor((255 + 0x00) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0x65) / 2), Math.floor((255 + 0xa4) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [Math.floor((255 + 0x75) / 2), Math.floor((255 + 0x50) / 2), Math.floor((255 + 0x7b) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [Math.floor((255 + 0x06) / 2), Math.floor((255 + 0x98) / 2), Math.floor((255 + 0x9a) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [Math.floor((255 + 0xd3) / 2), Math.floor((255 + 0xd7) / 2), Math.floor((255 + 0xcf) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [Math.floor((255 + 0x55) / 2), Math.floor((255 + 0x57) / 2), Math.floor((255 + 0x53) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 2), [Math.floor((255 + 0xef) / 2), Math.floor((255 + 0x29) / 2), Math.floor((255 + 0x29) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 2), [Math.floor((255 + 0x8a) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0x34) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 2), [Math.floor((255 + 0xfc) / 2), Math.floor((255 + 0xe9) / 2), Math.floor((255 + 0x4f) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 2), [Math.floor((255 + 0x72) / 2), Math.floor((255 + 0x9f) / 2), Math.floor((255 + 0xcf) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [Math.floor((255 + 0xad) / 2), Math.floor((255 + 0x7f) / 2), Math.floor((255 + 0xa8) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [Math.floor((255 + 0x34) / 2), Math.floor((255 + 0xe2) / 2), Math.floor((255 + 0xe2) / 2), 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xee) / 2), Math.floor((255 + 0xec) / 2), 255]); + // Setting and check for minimum contrast values, note that these are not + // exact to the contrast ratio, if the increase luminance algorithm + // changes then these will probably fail + await ctx.value.page.evaluate(`window.term.options.minimumContrastRatio = 10;`); + frameDetails = undefined; + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [150, 153, 154, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [229, 127, 127, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [63, 124, 4, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 1), [127, 104, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 1), [153, 178, 209, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 1), [186, 167, 189, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 1), [4, 122, 124, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 1), [110, 112, 108, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [170, 171, 169, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 2), [215, 36, 36, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 2), [72, 117, 25, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 4, 2), [117, 109, 36, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 5, 2), [72, 103, 135, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 6, 2), [125, 91, 121, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 7, 2), [25, 117, 117, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 8, 2), [111, 111, 110, 255]); + }); + }); + + test.describe('selectionBackground', async () => { + test('should resolve the inverse foreground color based on the original background color, not the selection', async () => { + const theme: ITheme = { + foreground: '#FF0000', + background: '#00FF00', + selectionBackground: '#0000FF' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.write( ` █\x1b[7m█\x1b[0m`); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 255, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [255, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [0, 255, 0, 255]); + await ctx.value.page.evaluate(`window.term.selectAll()`); + frameDetails = undefined; + // Selection only cell needs to be first to ensure renderer has kicked in + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 255, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [255, 0, 0, 255]); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [0, 255, 0, 255]); + }); + }); + + test.describe('allowTransparency', async () => { + test.beforeEach(() => ctx.value.page.evaluate(`term.options.allowTransparency = true`)); + + test('transparent background inverse', async () => { + const theme: ITheme = { + background: '#ff000080' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + const data = `\x1b[7m█\x1b[0m`; + await ctx.value.proxy.write( data); + // Inverse background should be opaque + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [255, 0, 0, 255]); + }); + }); + + test.describe('selectionForeground', () => { + test('transparent background inverse', async () => { + const theme: ITheme = { + selectionForeground: '#ff0000' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + const data = `\x1b[7m█\x1b[0m`; + await ctx.value.proxy.write( data); + await ctx.value.page.evaluate(`window.term.selectAll()`); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [255, 0, 0, 255]); + }); + }); + + test.describe('decoration color overrides', async () => { + test('foregroundColor', async () => { + await ctx.value.page.evaluate(` + const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); + window.term.registerDecoration({ + marker, + foregroundColor: '#ff0000', + backgroundColor: '#0000ff' + }); + `); + const data = `█`; + await ctx.value.proxy.write( data); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [255, 0, 0, 255]); + }); + test('foregroundColor should ignore inverse', async () => { + await ctx.value.page.evaluate(` + const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); + window.term.registerDecoration({ + marker, + foregroundColor: '#ff0000', + backgroundColor: '#0000ff' + }); + `); + const data = `\x1b[7m█\x1b[0m`; + await ctx.value.proxy.write( data); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [255, 0, 0, 255]); + }); + test('foregroundColor should ignore inverse (only fg on decoration)', async () => { + await ctx.value.page.evaluate(` + const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); + window.term.registerDecoration({ + marker, + width: 2, + foregroundColor: '#ff0000' + }); + `); + const data = `\x1b[7m█ \x1b[0m`; + await ctx.value.proxy.write( data); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [255, 0, 0, 255]); // inverse foreground of '█' should be decoration fg override + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [255, 255, 255, 255]); // inverse background of ' ' should be default foreground + }); + test('backgroundColor', async () => { + await ctx.value.page.evaluate(` + const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); + window.term.registerDecoration({ + marker, + foregroundColor: '#ff0000', + backgroundColor: '#0000ff' + }); + `); + const data = ` `; + await ctx.value.proxy.write( data); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 255, 255]); + }); + test('backgroundColor should ignore inverse', async () => { + await ctx.value.page.evaluate(` + const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); + window.term.registerDecoration({ + marker, + foregroundColor: '#ff0000', + backgroundColor: '#0000ff' + }); + `); + const data = `\x1b[7m \x1b[0m`; + await ctx.value.proxy.write( data); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 255, 255]); + }); + test('backgroundColor should ignore inverse (only bg on decoration)', async () => { + const data = `\x1b[7m█ \x1b[0m`; + await ctx.value.proxy.write( data); + await ctx.value.page.evaluate(` + const marker = window.term.registerMarker(-window.term.buffer.active.cursorY); + window.term.registerDecoration({ + marker, + width: 2, + backgroundColor: '#0000ff' + }); + `); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]); // inverse foreground of '█' should be default + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [0, 0, 255, 255]); // inverse background of ' ' should be decoration bg override + }); + }); +} + +/** + * Gets the color of the pixel in the center of a cell. + * @param ctx The test context. + * @param col The 1-based column index to get the color for. + * @param row The 1-based row index to get the color for. + */ +function getCellColor(ctx: ITestContext, col: number, row: number): MaybeAsync<[red: number, green: number, blue: number, alpha: number]> { + if (!frameDetails) { + return getFrameDetails(ctx).then(frameDetails => getCellColorInner(frameDetails, col, row)); + } + return getCellColorInner(frameDetails, col, row); +} + +let frameDetails: { cols: number, rows: number, decoded: IImage32 } | undefined = undefined; +async function getFrameDetails(ctx: ITestContext): Promise<{ cols: number, rows: number, decoded: IImage32 }> { + const screenshotOptions: LocatorScreenshotOptions | undefined = process.env.DEBUG ? { path: 'out-test/playwright/screenshot.png' } : undefined; + const buffer = await ctx.page.locator('#terminal-container .xterm-screen').screenshot(screenshotOptions); + frameDetails = { + cols: await ctx.proxy.cols, + rows: await ctx.proxy.rows, + decoded: (await decodePng(buffer, { force32: true })).image + }; + return frameDetails; +} + +function getCellColorInner(frameDetails: { cols: number, rows: number, decoded: IImage32 }, col: number, row: number): [red: number, green: number, blue: number, alpha: number] { + const cellSize = { + width: frameDetails.decoded.width / frameDetails.cols, + height: frameDetails.decoded.height / frameDetails.rows + }; + const x = Math.floor((col - 1/* 1- to 0-based index */ + 0.5/* middle of cell */) * cellSize.width); + const y = Math.floor((row - 1/* 1- to 0-based index */ + 0.5/* middle of cell */) * cellSize.height); + const i = (y * frameDetails.decoded.width + x) * 4/* 4 channels per pixel */; + return Array.from(frameDetails.decoded.data.slice(i, i + 4)) as [number, number, number, number]; +} + +const COLORS_16_TO_255 = [ + '#000000', '#00005f', '#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f', '#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f', '#008787', '#0087af', + '#0087d7', '#0087ff', '#00af00', '#00af5f', '#00af87', '#00afaf', '#00afd7', '#00afff', '#00d700', '#00d75f', '#00d787', '#00d7af', '#00d7d7', '#00d7ff', '#00ff00', '#00ff5f', + '#00ff87', '#00ffaf', '#00ffd7', '#00ffff', '#5f0000', '#5f005f', '#5f0087', '#5f00af', '#5f00d7', '#5f00ff', '#5f5f00', '#5f5f5f', '#5f5f87', '#5f5faf', '#5f5fd7', '#5f5fff', + '#5f8700', '#5f875f', '#5f8787', '#5f87af', '#5f87d7', '#5f87ff', '#5faf00', '#5faf5f', '#5faf87', '#5fafaf', '#5fafd7', '#5fafff', '#5fd700', '#5fd75f', '#5fd787', '#5fd7af', + '#5fd7d7', '#5fd7ff', '#5fff00', '#5fff5f', '#5fff87', '#5fffaf', '#5fffd7', '#5fffff', '#870000', '#87005f', '#870087', '#8700af', '#8700d7', '#8700ff', '#875f00', '#875f5f', + '#875f87', '#875faf', '#875fd7', '#875fff', '#878700', '#87875f', '#878787', '#8787af', '#8787d7', '#8787ff', '#87af00', '#87af5f', '#87af87', '#87afaf', '#87afd7', '#87afff', + '#87d700', '#87d75f', '#87d787', '#87d7af', '#87d7d7', '#87d7ff', '#87ff00', '#87ff5f', '#87ff87', '#87ffaf', '#87ffd7', '#87ffff', '#af0000', '#af005f', '#af0087', '#af00af', + '#af00d7', '#af00ff', '#af5f00', '#af5f5f', '#af5f87', '#af5faf', '#af5fd7', '#af5fff', '#af8700', '#af875f', '#af8787', '#af87af', '#af87d7', '#af87ff', '#afaf00', '#afaf5f', + '#afaf87', '#afafaf', '#afafd7', '#afafff', '#afd700', '#afd75f', '#afd787', '#afd7af', '#afd7d7', '#afd7ff', '#afff00', '#afff5f', '#afff87', '#afffaf', '#afffd7', '#afffff', + '#d70000', '#d7005f', '#d70087', '#d700af', '#d700d7', '#d700ff', '#d75f00', '#d75f5f', '#d75f87', '#d75faf', '#d75fd7', '#d75fff', '#d78700', '#d7875f', '#d78787', '#d787af', + '#d787d7', '#d787ff', '#d7af00', '#d7af5f', '#d7af87', '#d7afaf', '#d7afd7', '#d7afff', '#d7d700', '#d7d75f', '#d7d787', '#d7d7af', '#d7d7d7', '#d7d7ff', '#d7ff00', '#d7ff5f', + '#d7ff87', '#d7ffaf', '#d7ffd7', '#d7ffff', '#ff0000', '#ff005f', '#ff0087', '#ff00af', '#ff00d7', '#ff00ff', '#ff5f00', '#ff5f5f', '#ff5f87', '#ff5faf', '#ff5fd7', '#ff5fff', + '#ff8700', '#ff875f', '#ff8787', '#ff87af', '#ff87d7', '#ff87ff', '#ffaf00', '#ffaf5f', '#ffaf87', '#ffafaf', '#ffafd7', '#ffafff', '#ffd700', '#ffd75f', '#ffd787', '#ffd7af', + '#ffd7d7', '#ffd7ff', '#ffff00', '#ffff5f', '#ffff87', '#ffffaf', '#ffffd7', '#ffffff', '#080808', '#121212', '#1c1c1c', '#262626', '#303030', '#3a3a3a', '#444444', '#4e4e4e', + '#585858', '#626262', '#6c6c6c', '#767676', '#808080', '#8a8a8a', '#949494', '#9e9e9e', '#a8a8a8', '#b2b2b2', '#bcbcbc', '#c6c6c6', '#d0d0d0', '#dadada', '#e4e4e4', '#eeeeee' +];