diff --git a/addons/addon-search/test/SearchAddon.api.ts b/addons/addon-search/test/SearchAddon.api.ts deleted file mode 100644 index 92fd6b29..00000000 --- a/addons/addon-search/test/SearchAddon.api.ts +++ /dev/null @@ -1,510 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { assert } from 'chai'; -import { readFile } from 'fs'; -import { resolve } from 'path'; -import { openTerminal, writeSync, launchBrowser, timeout } from '../../../out-test/api/TestUtils'; -import { Browser, Page } from '@playwright/test'; - -const APP = 'http://127.0.0.1:3001/test'; - -let browser: Browser; -let page: Page; -const width = 800; -const height = 600; - -describe('Search Tests', function (): void { - before(async function (): Promise { - browser = await launchBrowser(); - page = await (await browser.newContext()).newPage(); - await page.setViewportSize({ width, height }); - await page.goto(APP); - await openTerminal(page); - }); - - after(() => { - browser.close(); - }); - - beforeEach(async () => { - await page.evaluate(` - window.term.reset() - window.search?.dispose(); - window.search = new SearchAddon(); - window.term.loadAddon(window.search); - `); - }); - - it('Simple Search', async () => { - await writeSync(page, 'dafhdjfldshafhldsahfkjhldhjkftestlhfdsakjfhdjhlfdsjkafhjdlk'); - assert.deepEqual(await page.evaluate(`window.search.findNext('test')`), true); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'test'); - }); - - it('Scrolling Search', async () => { - let dataString = ''; - for (let i = 0; i < 100; i++) { - if (i === 52) { - dataString += '$^1_3{}test$#'; - } - dataString += makeData(50); - } - await writeSync(page, dataString); - assert.deepEqual(await page.evaluate(`window.search.findNext('$^1_3{}test$#')`), true); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), '$^1_3{}test$#'); - }); - it('Incremental Find Previous', async () => { - await page.evaluate(`window.term.writeln('package.jsonc\\n')`); - await writeSync(page, 'package.json pack package.lock'); - await page.evaluate(`window.search.findPrevious('pack', {incremental: true})`); - let line: string = await page.evaluate(`window.term.buffer.active.getLine(window.term.getSelectionPosition().start.y).translateToString()`); - let selectionPosition: { start: { x: number, y: number }, end: { x: number, y: number } } = await page.evaluate(`window.term.getSelectionPosition()`); - // We look further ahead in the line to ensure that pack was selected from package.lock - assert.deepEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x + 8), 'package.lock'); - await page.evaluate(`window.search.findPrevious('package.j', {incremental: true})`); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x + 3), 'package.json'); - await page.evaluate(`window.search.findPrevious('package.jsonc', {incremental: true})`); - // We have to reevaluate line because it should have switched starting rows at this point - line = await page.evaluate(`window.term.buffer.active.getLine(window.term.getSelectionPosition().start.y).translateToString()`); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x), 'package.jsonc'); - }); - it('Incremental Find Next', async () => { - await page.evaluate(`window.term.writeln('package.lock pack package.json package.ups\\n')`); - await writeSync(page, 'package.jsonc'); - await page.evaluate(`window.search.findNext('pack', {incremental: true})`); - let line: string = await page.evaluate(`window.term.buffer.active.getLine(window.term.getSelectionPosition().start.y).translateToString()`); - let selectionPosition: { start: { x: number, y: number }, end: { x: number, y: number } } = await page.evaluate(`window.term.getSelectionPosition()`); - // We look further ahead in the line to ensure that pack was selected from package.lock - assert.deepEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x + 8), 'package.lock'); - await page.evaluate(`window.search.findNext('package.j', {incremental: true})`); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x + 3), 'package.json'); - await page.evaluate(`window.search.findNext('package.jsonc', {incremental: true})`); - // We have to reevaluate line because it should have switched starting rows at this point - line = await page.evaluate(`window.term.buffer.active.getLine(window.term.getSelectionPosition().start.y).translateToString()`); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x), 'package.jsonc'); - }); - it('Simple Regex', async () => { - await writeSync(page, 'abc123defABCD'); - await page.evaluate(`window.search.findNext('[a-z]+', {regex: true})`); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); - await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD'); - }); - - it('Search for single result twice should not unselect it', async () => { - await writeSync(page, 'abc def'); - assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); - assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); - }); - - it('Search for result bounding with wide unicode chars', async () => { - await writeSync(page, 'δΈ­ζ–‡xxπ„žπ„ž'); - assert.deepEqual(await page.evaluate(`window.search.findNext('δΈ­')`), true); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'δΈ­'); - assert.deepEqual(await page.evaluate(`window.search.findNext('xx')`), true); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'xx'); - assert.deepEqual(await page.evaluate(`window.search.findNext('π„ž')`), true); - assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'π„ž'); - assert.deepEqual(await page.evaluate(`window.search.findNext('π„ž')`), true); - assert.deepEqual(await page.evaluate(`window.term.getSelectionPosition()`), { - start: { - x: 7, - y: 0 - }, - end: { - x: 8, - y: 0 - } - }); - }); - - describe('onDidChangeResults', async () => { - describe('findNext', () => { - it('should not fire unless the decorations option is set', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - await writeSync(page, 'abc'); - assert.strictEqual(await page.evaluate(`window.search.findNext('a')`), true); - assert.strictEqual(await page.evaluate('window.calls.length'), 0); - assert.strictEqual(await page.evaluate(`window.search.findNext('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.strictEqual(await page.evaluate('window.calls.length'), 1); - }); - it('should fire with correct event values', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - await writeSync(page, 'abc bc c'); - assert.strictEqual(await page.evaluate(`window.search.findNext('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1, resultIndex: 0 } - ]); - assert.strictEqual(await page.evaluate(`window.search.findNext('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 } - ]); - assert.strictEqual(await page.evaluate(`window.search.findNext('d', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 0, resultIndex: -1 } - ]); - assert.strictEqual(await page.evaluate(`window.search.findNext('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.strictEqual(await page.evaluate(`window.search.findNext('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.strictEqual(await page.evaluate(`window.search.findNext('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 0, resultIndex: -1 }, - { resultCount: 3, resultIndex: 0 }, - { resultCount: 3, resultIndex: 1 }, - { resultCount: 3, resultIndex: 2 } - ]); - }); - it('should fire with correct event values (incremental)', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - await writeSync(page, 'd abc aabc d'); - assert.deepStrictEqual(await page.evaluate(`window.search.findNext('a', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 0 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findNext('ab', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findNext('abc', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findNext('abc', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 1 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findNext('d', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 1 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findNext('abcd', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 0, resultIndex: -1 } - ]); - }); - it('should fire with more than 1k matches', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - const data = ('a bc'.repeat(10) + '\\n\\r').repeat(150); - await writeSync(page, data); - assert.strictEqual(await page.evaluate(`window.search.findNext('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1000, resultIndex: 0 } - ]); - assert.strictEqual(await page.evaluate(`window.search.findNext('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1000, resultIndex: 0 }, - { resultCount: 1000, resultIndex: 1 } - ]); - assert.strictEqual(await page.evaluate(`window.search.findNext('bc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1000, resultIndex: 0 }, - { resultCount: 1000, resultIndex: 1 }, - { resultCount: 1000, resultIndex: 1 } - ]); - }); - it('should fire when writing to terminal', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - await writeSync(page, 'abc bc c\\n\\r'.repeat(2)); - assert.strictEqual(await page.evaluate(`window.search.findNext('abc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 2, resultIndex: 0 } - ]); - await writeSync(page, 'abc bc c\\n\\r'); - await timeout(300); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 2, resultIndex: 0 }, - { resultCount: 3, resultIndex: 0 } - ]); - }); - }); - describe('findPrevious', () => { - it('should not fire unless the decorations option is set', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - await writeSync(page, 'abc'); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('a')`), true); - assert.strictEqual(await page.evaluate('window.calls.length'), 0); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.strictEqual(await page.evaluate('window.calls.length'), 1); - }); - it('should fire with correct event values', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - await writeSync(page, 'abc bc c'); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1, resultIndex: 0 } - ]); - await page.evaluate(`window.term.clearSelection()`); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1, resultIndex: 0 }, - { resultCount: 2, resultIndex: 1 } - ]); - await timeout(2000); - assert.strictEqual(await page.evaluate(`debugger; window.search.findPrevious('d', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1, resultIndex: 0 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 0, resultIndex: -1 } - ]); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1, resultIndex: 0 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 0, resultIndex: -1 }, - { resultCount: 3, resultIndex: 2 }, - { resultCount: 3, resultIndex: 1 }, - { resultCount: 3, resultIndex: 0 } - ]); - }); - it('should fire with correct event values (incremental)', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - await writeSync(page, 'd abc aabc d'); - assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('a', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 2 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('ab', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 2 }, - { resultCount: 2, resultIndex: 1 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('abc', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 2 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 1 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('abc', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 2 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 0 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('d', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 2 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 1 } - ]); - assert.deepStrictEqual(await page.evaluate(`window.search.findPrevious('abcd', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 3, resultIndex: 2 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 2, resultIndex: 0 }, - { resultCount: 2, resultIndex: 1 }, - { resultCount: 0, resultIndex: -1 } - ]); - }); - it('should fire with more than 1k matches', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - const data = ('a bc'.repeat(10) + '\\n\\r').repeat(150); - await writeSync(page, data); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1000, resultIndex: -1 } - ]); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1000, resultIndex: -1 }, - { resultCount: 1000, resultIndex: -1 } - ]); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('bc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 1000, resultIndex: -1 }, - { resultCount: 1000, resultIndex: -1 }, - { resultCount: 1000, resultIndex: -1 } - ]); - }); - it('should fire when writing to terminal', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - await writeSync(page, 'abc bc c\\n\\r'.repeat(2)); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('abc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 2, resultIndex: 1 } - ]); - await writeSync(page, 'abc bc c\\n\\r'); - await timeout(300); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 2, resultIndex: 1 }, - { resultCount: 3, resultIndex: 1 } - ]); - }); - }); - }); - - describe('Regression tests', () => { - describe('#2444 wrapped line content not being found', () => { - let fixture: string; - before(async () => { - const rawFixture = await new Promise(r => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => r(data))); - if (process.platform === 'win32') { - fixture = rawFixture.toString() - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r'); - } else { - fixture = rawFixture.toString() - .replace(/\n/g, '\\n\\r'); - } - fixture = fixture - .replace(/'/g, `\\'`); - }); - it('should find all occurrences using findNext', async () => { - await writeSync(page, fixture); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - let selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 24, y: 53 }, end: { x: 30, y: 53 } }); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 24, y: 76 }, end: { x: 30, y: 76 } }); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 24, y: 96 }, end: { x: 30, y: 96 } }); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 1, y: 114 }, end: { x: 7, y: 114 } }); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 11, y: 115 }, end: { x: 17, y: 115 } }); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 1, y: 126 }, end: { x: 7, y: 126 } }); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 11, y: 127 }, end: { x: 17, y: 127 } }); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 1, y: 135 }, end: { x: 7, y: 135 } }); - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 11, y: 136 }, end: { x: 17, y: 136 } }); - // Wrap around to first result - assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 24, y: 53 }, end: { x: 30, y: 53 } }); - }); - - it('should y all occurrences using findPrevious', async () => { - await writeSync(page, fixture); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - let selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 11, y: 136 }, end: { x: 17, y: 136 } }); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 1, y: 135 }, end: { x: 7, y: 135 } }); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 11, y: 127 }, end: { x: 17, y: 127 } }); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 1, y: 126 }, end: { x: 7, y: 126 } }); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 11, y: 115 }, end: { x: 17, y: 115 } }); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 1, y: 114 }, end: { x: 7, y: 114 } }); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 24, y: 96 }, end: { x: 30, y: 96 } }); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 24, y: 76 }, end: { x: 30, y: 76 } }); - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 24, y: 53 }, end: { x: 30, y: 53 } }); - // Wrap around to first result - assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true); - selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); - assert.deepEqual(selectionPosition, { start: { x: 11, y: 136 }, end: { x: 17, y: 136 } }); - }); - }); - }); - describe('#3834 lines with null characters before search terms', () => { - // This case can be triggered by the prompt when using starship under conpty - it('should find all matches on a line containing null characters', async () => { - await page.evaluate(` - window.calls = []; - window.search.onDidChangeResults(e => window.calls.push(e)); - `); - // Move cursor forward 1 time to create a null character, as opposed to regular whitespace - await writeSync(page, '\\x1b[CHi Hi'); - assert.strictEqual(await page.evaluate(`window.search.findPrevious('h', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); - assert.deepStrictEqual(await page.evaluate('window.calls'), [ - { resultCount: 2, resultIndex: 1 } - ]); - }); - }); -}); - -function makeData(length: number): string { - let result = ''; - const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * characters.length)); - } - return result; -} diff --git a/addons/addon-search/test/SearchAddon.test.ts b/addons/addon-search/test/SearchAddon.test.ts new file mode 100644 index 00000000..0adf2bfe --- /dev/null +++ b/addons/addon-search/test/SearchAddon.test.ts @@ -0,0 +1,493 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { deepStrictEqual, strictEqual } from 'assert'; +import { ITestContext, createTestContext, openTerminal, timeout } from '../../../out-test/playwright/TestUtils'; +import test from '@playwright/test'; +import { readFile } from 'fs'; +import { resolve } from 'path'; + +let ctx: ITestContext; +test.beforeAll(async ({ browser }) => { + ctx = await createTestContext(browser); + await openTerminal(ctx); + await ctx.page.evaluate(` + window.addon = new window.SearchAddon(); + window.term.loadAddon(window.addon); + `); +}); +test.afterAll(async () => await ctx.page.close()); + +test.describe('Search Tests', () => { + + test.beforeEach(async () => { + await ctx.page.evaluate(` + window.term.reset() + window.search?.dispose(); + window.search = new SearchAddon(); + window.term.loadAddon(window.search); + `); + }); + + test('Simple Search', async () => { + await ctx.proxy.write('dafhdjfldshafhldsahfkjhldhjkftestlhfdsakjfhdjhlfdsjkafhjdlk'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('test')`), true); + deepStrictEqual(await ctx.proxy.getSelection(), 'test'); + }); + + test('Scrolling Search', async () => { + let dataString = ''; + for (let i = 0; i < 100; i++) { + if (i === 52) { + dataString += '$^1_3{}test$#'; + } + dataString += makeData(50); + } + await ctx.proxy.write(dataString); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('$^1_3{}test$#')`), true); + deepStrictEqual(await ctx.proxy.getSelection(), '$^1_3{}test$#'); + }); + test('Incremental Find Previous', async () => { + await ctx.proxy.writeln(`package.jsonc\n`); + await ctx.proxy.write('package.json pack package.lock'); + await ctx.page.evaluate(`window.search.findPrevious('pack', {incremental: true})`); + let selectionPosition: { start: { x: number, y: number }, end: { x: number, y: number } } = (await ctx.proxy.getSelectionPosition())!; + let line: string = await (await ctx.proxy.buffer.active.getLine(selectionPosition.start.y))!.translateToString(); + // We look further ahead in the line to ensure that pack was selected from package.lock + deepStrictEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x + 8), 'package.lock'); + await ctx.page.evaluate(`window.search.findPrevious('package.j', {incremental: true})`); + selectionPosition = (await ctx.proxy.getSelectionPosition())!; + deepStrictEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x + 3), 'package.json'); + await ctx.page.evaluate(`window.search.findPrevious('package.jsonc', {incremental: true})`); + // We have to reevaluate line because it should have switched starting rows at this point + selectionPosition = (await ctx.proxy.getSelectionPosition())!; + line = await (await ctx.proxy.buffer.active.getLine(selectionPosition.start.y))!.translateToString(); + deepStrictEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x), 'package.jsonc'); + }); + test('Incremental Find Next', async () => { + await ctx.proxy.writeln(`package.lock pack package.json package.ups\n`); + await ctx.proxy.write('package.jsonc'); + await ctx.page.evaluate(`window.search.findNext('pack', {incremental: true})`); + let selectionPosition: { start: { x: number, y: number }, end: { x: number, y: number } } = (await ctx.proxy.getSelectionPosition())!; + let line: string = await (await ctx.proxy.buffer.active.getLine(selectionPosition.start.y))!.translateToString(); + // We look further ahead in the line to ensure that pack was selected from package.lock + deepStrictEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x + 8), 'package.lock'); + await ctx.page.evaluate(`window.search.findNext('package.j', {incremental: true})`); + selectionPosition = (await ctx.proxy.getSelectionPosition())!; + deepStrictEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x + 3), 'package.json'); + await ctx.page.evaluate(`window.search.findNext('package.jsonc', {incremental: true})`); + // We have to reevaluate line because it should have switched starting rows at this point + selectionPosition = (await ctx.proxy.getSelectionPosition())!; + line = await (await ctx.proxy.buffer.active.getLine(selectionPosition.start.y))!.translateToString(); + deepStrictEqual(line.substring(selectionPosition.start.x, selectionPosition.end.x), 'package.jsonc'); + }); + test('Simple Regex', async () => { + await ctx.proxy.write('abc123defABCD'); + await ctx.page.evaluate(`window.search.findNext('[a-z]+', {regex: true})`); + deepStrictEqual(await ctx.proxy.getSelection(), 'abc'); + await ctx.page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`); + deepStrictEqual(await ctx.proxy.getSelection(), 'ABCD'); + }); + + test('Search for single result twice should not unselect it', async () => { + await ctx.proxy.write('abc def'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('abc')`), true); + deepStrictEqual(await ctx.proxy.getSelection(), 'abc'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('abc')`), true); + deepStrictEqual(await ctx.proxy.getSelection(), 'abc'); + }); + + test('Search for result bounding with wide unicode chars', async () => { + await ctx.proxy.write('δΈ­ζ–‡xxπ„žπ„ž'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('δΈ­')`), true); + deepStrictEqual(await ctx.proxy.getSelection(), 'δΈ­'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('xx')`), true); + deepStrictEqual(await ctx.proxy.getSelection(), 'xx'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('π„ž')`), true); + deepStrictEqual(await ctx.proxy.getSelection(), 'π„ž'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('π„ž')`), true); + deepStrictEqual(await ctx.proxy.getSelectionPosition(), { + start: { + x: 7, + y: 0 + }, + end: { + x: 8, + y: 0 + } + }); + }); + + test.describe('onDidChangeResults', async () => { + test.describe('findNext', () => { + test('should not fire unless the decorations option is set', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.write('abc'); + strictEqual(await ctx.page.evaluate(`window.search.findNext('a')`), true); + strictEqual(await ctx.page.evaluate('window.calls.length'), 0); + strictEqual(await ctx.page.evaluate(`window.search.findNext('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + strictEqual(await ctx.page.evaluate('window.calls.length'), 1); + }); + test('should fire with correct event values', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.write('abc bc c'); + strictEqual(await ctx.page.evaluate(`window.search.findNext('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1, resultIndex: 0 } + ]); + strictEqual(await ctx.page.evaluate(`window.search.findNext('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 } + ]); + strictEqual(await ctx.page.evaluate(`window.search.findNext('d', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 0, resultIndex: -1 } + ]); + strictEqual(await ctx.page.evaluate(`window.search.findNext('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + strictEqual(await ctx.page.evaluate(`window.search.findNext('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + strictEqual(await ctx.page.evaluate(`window.search.findNext('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 0, resultIndex: -1 }, + { resultCount: 3, resultIndex: 0 }, + { resultCount: 3, resultIndex: 1 }, + { resultCount: 3, resultIndex: 2 } + ]); + }); + test('should fire with correct event values (incremental)', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.write('d abc aabc d'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('a', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 0 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('ab', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('abc', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('abc', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 1 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('d', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 1 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('abcd', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 0, resultIndex: -1 } + ]); + }); + test('should fire with more than 1k matches', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + const data = ('a bc'.repeat(10) + '\\n\\r').repeat(150); + await ctx.proxy.write(data); + strictEqual(await ctx.page.evaluate(`window.search.findNext('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1000, resultIndex: 0 } + ]); + strictEqual(await ctx.page.evaluate(`window.search.findNext('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1000, resultIndex: 0 }, + { resultCount: 1000, resultIndex: 1 } + ]); + strictEqual(await ctx.page.evaluate(`window.search.findNext('bc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1000, resultIndex: 0 }, + { resultCount: 1000, resultIndex: 1 }, + { resultCount: 1000, resultIndex: 1 } + ]); + }); + test('should fire when writing to terminal', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.write('abc bc c\\n\\r'.repeat(2)); + strictEqual(await ctx.page.evaluate(`window.search.findNext('abc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 2, resultIndex: 0 } + ]); + await ctx.proxy.write('abc bc c\\n\\r'); + await timeout(300); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 2, resultIndex: 0 }, + { resultCount: 3, resultIndex: 0 } + ]); + }); + }); + test.describe('findPrevious', () => { + test('should not fire unless the decorations option is set', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.write('abc'); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('a')`), true); + strictEqual(await ctx.page.evaluate('window.calls.length'), 0); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + strictEqual(await ctx.page.evaluate('window.calls.length'), 1); + }); + test('should fire with correct event values', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.write('abc bc c'); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1, resultIndex: 0 } + ]); + await ctx.page.evaluate(`window.term.clearSelection()`); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('b', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1, resultIndex: 0 }, + { resultCount: 2, resultIndex: 1 } + ]); + await timeout(2000); + strictEqual(await ctx.page.evaluate(`debugger; window.search.findPrevious('d', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1, resultIndex: 0 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 0, resultIndex: -1 } + ]); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('c', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1, resultIndex: 0 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 0, resultIndex: -1 }, + { resultCount: 3, resultIndex: 2 }, + { resultCount: 3, resultIndex: 1 }, + { resultCount: 3, resultIndex: 0 } + ]); + }); + test('should fire with correct event values (incremental)', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.write('d abc aabc d'); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('a', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 2 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('ab', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 2 }, + { resultCount: 2, resultIndex: 1 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('abc', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 2 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 1 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('abc', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 2 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 0 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('d', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 2 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 1 } + ]); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('abcd', { incremental: true, decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), false); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 3, resultIndex: 2 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 2, resultIndex: 0 }, + { resultCount: 2, resultIndex: 1 }, + { resultCount: 0, resultIndex: -1 } + ]); + }); + test('should fire with more than 1k matches', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + const data = ('a bc'.repeat(10) + '\\n\\r').repeat(150); + await ctx.proxy.write(data); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1000, resultIndex: -1 } + ]); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('a', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1000, resultIndex: -1 }, + { resultCount: 1000, resultIndex: -1 } + ]); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('bc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 1000, resultIndex: -1 }, + { resultCount: 1000, resultIndex: -1 }, + { resultCount: 1000, resultIndex: -1 } + ]); + }); + test('should fire when writing to terminal', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + await ctx.proxy.write('abc bc c\\n\\r'.repeat(2)); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('abc', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 2, resultIndex: 1 } + ]); + await ctx.proxy.write('abc bc c\\n\\r'); + await timeout(300); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 2, resultIndex: 1 }, + { resultCount: 3, resultIndex: 1 } + ]); + }); + }); + }); + + test.describe('Regression tests', () => { + test.describe('#2444 wrapped line content not being found', () => { + let fixture: string; + test.beforeAll(async () => { + fixture = (await new Promise(r => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => r(data)))).toString(); + }); + test('should find all occurrences using findNext', async () => { + await ctx.proxy.write(fixture); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + let selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 24, y: 53 }, end: { x: 30, y: 53 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 24, y: 76 }, end: { x: 30, y: 76 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 24, y: 96 }, end: { x: 30, y: 96 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 1, y: 114 }, end: { x: 7, y: 114 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 11, y: 115 }, end: { x: 17, y: 115 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 1, y: 126 }, end: { x: 7, y: 126 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 11, y: 127 }, end: { x: 17, y: 127 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 1, y: 135 }, end: { x: 7, y: 135 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 11, y: 136 }, end: { x: 17, y: 136 } }); + // Wrap around to first result + deepStrictEqual(await ctx.page.evaluate(`window.search.findNext('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 24, y: 53 }, end: { x: 30, y: 53 } }); + }); + + test('should y all occurrences using findPrevious', async () => { + await ctx.proxy.write(fixture); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + let selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 11, y: 136 }, end: { x: 17, y: 136 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 1, y: 135 }, end: { x: 7, y: 135 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 11, y: 127 }, end: { x: 17, y: 127 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 1, y: 126 }, end: { x: 7, y: 126 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 11, y: 115 }, end: { x: 17, y: 115 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 1, y: 114 }, end: { x: 7, y: 114 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 24, y: 96 }, end: { x: 30, y: 96 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 24, y: 76 }, end: { x: 30, y: 76 } }); + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 24, y: 53 }, end: { x: 30, y: 53 } }); + // Wrap around to first result + deepStrictEqual(await ctx.page.evaluate(`window.search.findPrevious('opencv')`), true); + selectionPosition = await ctx.proxy.getSelectionPosition(); + deepStrictEqual(selectionPosition, { start: { x: 11, y: 136 }, end: { x: 17, y: 136 } }); + }); + }); + }); + test.describe('#3834 lines with null characters before search terms', () => { + // This case can be triggered by the prompt when using starship under conpty + test('should find all matches on a line containing null characters', async () => { + await ctx.page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + // Move cursor forward 1 time to create a null character, as opposed to regular whitespace + await ctx.proxy.write('\\x1b[CHi Hi'); + strictEqual(await ctx.page.evaluate(`window.search.findPrevious('h', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + deepStrictEqual(await ctx.page.evaluate('window.calls'), [ + { resultCount: 2, resultIndex: 1 } + ]); + }); + }); +}); + +function makeData(length: number): string { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return result; +} diff --git a/addons/addon-search/test/playwright.config.ts b/addons/addon-search/test/playwright.config.ts new file mode 100644 index 00000000..b0e565c5 --- /dev/null +++ b/addons/addon-search/test/playwright.config.ts @@ -0,0 +1,35 @@ +import { PlaywrightTestConfig } from '@playwright/test'; + +const config: PlaywrightTestConfig = { + testDir: '.', + timeout: 10000, + projects: [ + { + name: 'Chrome Stable', + use: { + browserName: 'chromium', + channel: 'chrome' + } + }, + { + name: 'Firefox Stable', + use: { + browserName: 'firefox' + } + }, + { + name: 'WebKit', + use: { + browserName: 'webkit' + } + } + ], + reporter: 'list', + webServer: { + command: 'npm run start-server-only', + port: 3000, + timeout: 120000, + reuseExistingServer: !process.env.CI + } +}; +export default config; diff --git a/addons/addon-search/test/tsconfig.json b/addons/addon-search/test/tsconfig.json index ee45e725..395be00d 100644 --- a/addons/addon-search/test/tsconfig.json +++ b/addons/addon-search/test/tsconfig.json @@ -9,15 +9,32 @@ "outDir": "../out-test", "sourceMap": true, "removeComments": true, + "baseUrl": ".", + "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ] + }, "strict": true, "types": [ - "../../../node_modules/@types/mocha", "../../../node_modules/@types/node", - "../../../out-test/api/TestUtils" + "../../../node_modules/@lunapaint/png-codec", + "../../../out-test/playwright/TestUtils" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" + ], + "references": [ + { + "path": "../../../src/common" + }, + { + "path": "../../../src/browser" + } ] } diff --git a/bin/test_playwright.js b/bin/test_playwright.js index f5621a18..34738122 100644 --- a/bin/test_playwright.js +++ b/bin/test_playwright.js @@ -20,6 +20,7 @@ while (argv.some(e => e.startsWith('--suite='))) { let configs = [ { name: 'core', path: 'out-test/playwright/playwright.config.js' }, { name: 'addon-canvas', path: 'addons/addon-canvas/out-test/playwright.config.js' }, + { name: 'addon-search', path: 'addons/addon-search/out-test/playwright.config.js' }, { name: 'addon-webgl', path: 'addons/addon-webgl/out-test/playwright.config.js' } ];