Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ jobs:
run: node common/scripts/install-run-rush.js build -t @visactor/vtable-search
- name: Test
run: node common/scripts/install-run-rush.js test -t @visactor/vtable-search
- name: Install Playwright Firefox
working-directory: ./packages/vtable
run: npx playwright install firefox
- name: Test Firefox paste
working-directory: ./packages/vtable
run: node ../../common/scripts/install-run-rushx.js test:firefox
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ stats.html
report.html

coverage/
playwright-report/
test-results/

*.local.ts

Expand Down
43 changes: 41 additions & 2 deletions common/config/rush/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions packages/vtable/__tests__/clipboard-paste.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// @ts-nocheck
import { ListTable, TABLE_EVENT_TYPE } from '../src';
import { createDiv, removeDom } from './dom';

global.__VERSION__ = 'none';

describe('clipboard paste', () => {
test('uses paste event data before the asynchronous Clipboard API', async () => {
const container = createDiv();
container.style.width = '600px';
container.style.height = '400px';

const clipboardDescriptor = Object.getOwnPropertyDescriptor(navigator, 'clipboard');
const read = jest.fn(() => Promise.reject(new Error('clipboard-read is unavailable')));
Object.defineProperty(navigator, 'clipboard', {
configurable: true,
value: { read }
});

const table = new ListTable(container, {
records: [{ name: 'before' }],
columns: [{ field: 'name', title: 'Name', editor: '' }],
keyboardOptions: {
pasteValueToCell: true
}
});

try {
table.selectCell(0, table.columnHeaderLevelCount);
const pasted = new Promise(resolve => {
table.on(TABLE_EVENT_TYPE.PASTED_DATA, resolve);
});
const pasteEvent = new Event('paste', { bubbles: true });
Object.defineProperty(pasteEvent, 'clipboardData', {
value: {
types: ['text/plain'],
getData: (type: string) => (type === 'text/plain' ? 'pasted in Firefox' : '')
}
});

table.getElement().dispatchEvent(pasteEvent);
await pasted;

expect(read).not.toHaveBeenCalled();
expect(table.getCellOriginValue(0, table.columnHeaderLevelCount)).toBe('pasted in Firefox');
} finally {
table.release();
removeDom(container);
if (clipboardDescriptor) {
Object.defineProperty(navigator, 'clipboard', clipboardDescriptor);
} else {
Reflect.deleteProperty(navigator, 'clipboard');
}
}
});
});
72 changes: 72 additions & 0 deletions packages/vtable/e2e/clipboard-paste.firefox.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { expect, test } from '@playwright/test';

interface Issue4699Table {
columnHeaderLevelCount: number;
getCellOriginValue: (col: number, row: number) => unknown;
getElement: () => HTMLElement;
selectCell: (col: number, row: number) => void;
}

interface Issue4699Window extends Window {
__issue4699ClipboardState: {
readCalls: number;
};
__issue4699Table: Issue4699Table;
}

test('pastes event clipboard data into a selected cell without an editor caret', async ({ page, browserName }) => {
test.skip(browserName !== 'firefox', 'Firefox-only clipboard regression');

await page.addInitScript(() => {
const state = { readCalls: 0 };
Object.defineProperty(window, '__issue4699ClipboardState', {
configurable: true,
value: state
});
Object.defineProperty(navigator, 'clipboard', {
configurable: true,
value: {
read: () => {
state.readCalls++;
return Promise.reject(new Error('Firefox clipboard-read is unavailable'));
}
}
});
});

await page.goto('/');
await page.waitForFunction(() => Boolean((window as unknown as Issue4699Window).__issue4699Table));

const activeElement = await page.evaluate(() => {
const testWindow = window as unknown as Issue4699Window;
const table = testWindow.__issue4699Table;
const row = table.columnHeaderLevelCount;
const clipboardData = new DataTransfer();
clipboardData.setData('text/plain', 'pasted in Firefox');
const pasteEvent = new ClipboardEvent('paste', {
bubbles: true,
cancelable: true
});
Object.defineProperty(pasteEvent, 'clipboardData', {
value: clipboardData
});

table.selectCell(0, row);
table.getElement().focus();
table.getElement().dispatchEvent(pasteEvent);
return document.activeElement?.tagName;
});

expect(activeElement).not.toMatch(/^(INPUT|TEXTAREA)$/);
await expect
.poll(() =>
page.evaluate(() => {
const table = (window as unknown as Issue4699Window).__issue4699Table;
return table.getCellOriginValue(0, table.columnHeaderLevelCount);
})
)
.toBe('pasted in Firefox');
await expect
.poll(() => page.evaluate(() => (window as unknown as Issue4699Window).__issue4699ClipboardState.readCalls))
.toBe(0);
});
19 changes: 19 additions & 0 deletions packages/vtable/e2e/fixtures/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ListTable } from '../../src';

const container = document.getElementById('vtable');
if (!container) {
throw new Error('Missing #vtable container');
}

const table = new ListTable(container, {
records: [{ value: 'before' }],
columns: [{ field: 'value', title: 'Value', editor: '' }],
keyboardOptions: {
pasteValueToCell: true
}
});

Object.defineProperty(window, '__issue4699Table', {
configurable: true,
value: table
});
15 changes: 15 additions & 0 deletions packages/vtable/e2e/fixtures/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<main>
<div id="vtable"></div>
</main>
<script type="module" src="./app.ts"></script>

<style>
html,
body,
main,
#vtable {
width: 100%;
height: 100%;
margin: 0;
}
</style>
15 changes: 15 additions & 0 deletions packages/vtable/e2e/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import path from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
define: {
__DEV__: true,
__VERSION__: JSON.stringify('e2e')
},
resolve: {
alias: {
'@src': path.resolve(__dirname, '../src'),
'@vutils-extension': path.resolve(__dirname, '../src/vutil-extension-temp')
}
}
});
5 changes: 4 additions & 1 deletion packages/vtable/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@
"demo": "vite serve examples",
"site-demo": "vite serve site-demo",
"compile": "tsc --noEmit",
"e2e:serve": "vite serve e2e/fixtures --config e2e/vite.config.ts --host 127.0.0.1 --port 3010",
"eslint": "eslint --debug --fix src/",
"build": "npm run fix-memory-limit && bundle --clean",
"dev": "bundle --clean -f es -w",
"start": "vite serve examples",
"test": "jest --silent",
"test:firefox": "playwright test --config playwright.config.ts",
"test-cov": "jest --coverage",
"ci": "ts-node --transpileOnly --skipProject ./scripts/trigger-test.ts",
"build:es": "bundle --clean -f es --ignorePostTasks",
Expand Down Expand Up @@ -120,7 +122,8 @@
"d3-hexbin": "^0.2.2",
"d3-hierarchy": "^3.1.1",
"@resvg/resvg-js": "^2.5.0",
"pikaday": "1.8.2"
"pikaday": "1.8.2",
"@playwright/test": "1.55.1"
},
"unpkg": "latest",
"unpkgFiles": [
Expand Down
24 changes: 24 additions & 0 deletions packages/vtable/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './e2e',
outputDir: './test-results',
reporter: 'list',
use: {
baseURL: 'http://127.0.0.1:3010',
trace: 'retain-on-failure'
},
projects: [
{
name: 'firefox',
use: {
...devices['Desktop Firefox']
}
}
],
webServer: {
command: 'rushx e2e:serve',
url: 'http://127.0.0.1:3010',
reuseExistingServer: !process.env.CI
}
});
2 changes: 1 addition & 1 deletion packages/vtable/tsconfig.eslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
]
}
},
"include": ["src", "__tests__", "examples", "site-demo"],
"include": ["src", "__tests__", "e2e", "examples", "site-demo", "playwright.config.ts"],
"exclude": ["bugserver-config"]
}
Loading