Skip to content

Commit 1f83eef

Browse files
authored
Merge pull request #1597 from tonypzy/migrate-pane-tests-playwright
feat: migrate cypress pane.js spec to playwright
2 parents 7586866 + 1d3ea55 commit 1f83eef

1 file changed

Lines changed: 273 additions & 0 deletions

File tree

playwright/tests/pane.spec.js

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/**
2+
* Copyright 2017-present, The Visdom Authors
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*
8+
*/
9+
10+
const { test, expect } = require('@playwright/test');
11+
const { runDemo } = require('../support/helpers');
12+
13+
const winSelector = '.layout .react-grid-item';
14+
const windowSelector = '.layout .window';
15+
const resizedSize = { height: 410, width: 307 };
16+
17+
const paneCases = [
18+
{
19+
type: 'TextPane',
20+
demo: 'text_basic',
21+
targetX: 263,
22+
size: { height: 290, width: 244 },
23+
},
24+
{
25+
type: 'ImagePane',
26+
demo: 'image_basic',
27+
targetX: 276,
28+
size: { height: 545, width: 256 },
29+
resetSize: { height: 545, width: 256 },
30+
},
31+
{ type: 'Line Plot', demo: 'plot_line_basic' },
32+
{ type: 'Bar Plot', demo: 'plot_bar_basic' },
33+
{ type: 'Scatter Plot', demo: 'plot_scatter_basic' },
34+
{ type: 'Surface Plot', demo: 'plot_surface_basic' },
35+
{ type: 'ROC Curve', demo: 'plot_roc_curve' },
36+
{ type: 'PR Curve', demo: 'plot_pr_curve' },
37+
{ type: 'Box Plot', demo: 'plot_special_boxplot' },
38+
{ type: 'Quiver Plot', demo: 'plot_special_quiver' },
39+
{ type: 'Violin Plot', demo: 'plot_violin_basic' },
40+
{
41+
type: 'Graph Plot',
42+
demo: 'plot_special_graph',
43+
targetX: 520,
44+
size: { height: 515, width: 500 },
45+
resetSize: { height: 515, width: 500 },
46+
},
47+
{ type: 'Sankey Plot', demo: 'plot_special_sankey' },
48+
{ type: 'Learning Curve', demo: 'plot_line_learning_curve' },
49+
{
50+
type: 'Matplotlib Plot',
51+
demo: 'misc_plot_matplot',
52+
targetX: 10,
53+
size: { height: 500, width: 622 },
54+
resetSize: { height: 500, width: 622 },
55+
},
56+
{ type: 'Latex Plot', demo: 'misc_plot_latex' },
57+
{
58+
type: 'Video Pane',
59+
demo: 'misc_video_tensor',
60+
targetX: 263,
61+
size: { height: 290, width: 244 },
62+
resetSize: { height: 290, width: 244 },
63+
},
64+
{
65+
type: 'Properties Pane',
66+
demo: 'properties_basic',
67+
targetX: 263,
68+
size: { height: 290, width: 244 },
69+
},
70+
{
71+
type: 'HTML Table',
72+
demo: 'html_table',
73+
targetX: 263,
74+
size: { height: 290, width: 244 },
75+
},
76+
{ type: 'Confusion Matrix', demo: 'plot_confusion_matrix_basic' },
77+
].map((paneCase) => {
78+
const size = paneCase.size || { height: 350, width: 370 };
79+
return {
80+
targetX: 390,
81+
resetSize: size,
82+
...paneCase,
83+
size,
84+
};
85+
});
86+
87+
function firstPane(page) {
88+
return page.locator(winSelector).first();
89+
}
90+
91+
async function dragMouse(page, locator, deltaX, deltaY = 0) {
92+
const box = await locator.boundingBox();
93+
if (!box) {
94+
throw new Error('Unable to find element box for drag target.');
95+
}
96+
97+
const startX = box.x + box.width / 2;
98+
const startY = box.y + box.height / 2;
99+
await page.mouse.move(startX, startY);
100+
await page.mouse.down();
101+
await page.mouse.move(startX + deltaX, startY + deltaY, { steps: 16 });
102+
await page.mouse.up();
103+
}
104+
105+
async function resizePane(page, pane, deltaX, deltaY) {
106+
const handle = pane.locator('.react-resizable-handle').first();
107+
const box = await handle.boundingBox();
108+
if (!box) {
109+
throw new Error('Unable to find pane resize handle.');
110+
}
111+
112+
const startX = box.x + box.width / 2;
113+
const startY = box.y + box.height / 2;
114+
await page.mouse.move(startX, startY);
115+
await page.mouse.down();
116+
await page.mouse.move(startX + deltaX, startY + deltaY, { steps: 8 });
117+
await page.mouse.up();
118+
}
119+
120+
async function getPaneTranslate(pane) {
121+
return pane.evaluate((element) => {
122+
const transform = window.getComputedStyle(element).transform;
123+
if (!transform || transform === 'none') {
124+
return { x: 0, y: 0 };
125+
}
126+
127+
const matrix = transform.match(/^matrix\((.+)\)$/);
128+
if (matrix) {
129+
const values = matrix[1].split(',').map(Number);
130+
return { x: values[4], y: values[5] };
131+
}
132+
133+
const matrix3d = transform.match(/^matrix3d\((.+)\)$/);
134+
if (matrix3d) {
135+
const values = matrix3d[1].split(',').map(Number);
136+
return { x: values[12], y: values[13] };
137+
}
138+
139+
throw new Error(`Unexpected transform value: ${transform}`);
140+
});
141+
}
142+
143+
async function expectPaneTranslate(pane, expectedX, expectedY) {
144+
await expect
145+
.poll(async () => {
146+
const { x, y } = await getPaneTranslate(pane);
147+
return Math.abs(x - expectedX) + Math.abs(y - expectedY);
148+
})
149+
.toBeLessThanOrEqual(1);
150+
}
151+
152+
async function expectPaneSize(pane, size) {
153+
await expect
154+
.poll(async () => {
155+
const currentSize = await pane.evaluate((element) => {
156+
const style = window.getComputedStyle(element);
157+
return {
158+
height: parseFloat(style.height),
159+
width: parseFloat(style.width),
160+
};
161+
});
162+
return (
163+
Math.abs(currentSize.height - size.height) +
164+
Math.abs(currentSize.width - size.width)
165+
);
166+
})
167+
.toBeLessThanOrEqual(2);
168+
}
169+
170+
test.describe('Test Pane Actions', () => {
171+
test.beforeEach(async ({ page }) => {
172+
await page.goto('/');
173+
});
174+
175+
for (const paneCase of paneCases) {
176+
test(`Pane actions on ${paneCase.type}`, async ({ page }) => {
177+
test.setTimeout(90000);
178+
179+
await test.step(`Open Single ${paneCase.type}`, async () => {
180+
await runDemo(page, paneCase.demo);
181+
await expect(page.locator(windowSelector)).toHaveCount(1);
182+
});
183+
184+
await test.step('Open Some More Panes', async () => {
185+
const env = `${paneCase.demo}_${Math.floor(Math.random() * 1e6)}`;
186+
await runDemo(page, paneCase.demo, { env: env, open: false });
187+
await runDemo(page, paneCase.demo, { env: env, open: false });
188+
await runDemo(page, paneCase.demo, { env: env, open: false });
189+
await runDemo(page, paneCase.demo, { env: env });
190+
await expect(page.locator(windowSelector)).toHaveCount(4);
191+
});
192+
193+
await test.step('Drag & Drop Pane to 2nd Position', async () => {
194+
const pane = firstPane(page);
195+
await expectPaneTranslate(pane, 10, 10);
196+
await dragMouse(page, pane.locator('.bar').first(), 600);
197+
await page.locator('[data-original-title="Repack"]').click();
198+
await expectPaneTranslate(pane, paneCase.targetX, 10);
199+
});
200+
201+
await test.step('Check Pane Size', async () => {
202+
await expectPaneSize(firstPane(page), paneCase.size);
203+
});
204+
205+
await test.step('Resize Pane', async () => {
206+
const pane = firstPane(page);
207+
await resizePane(
208+
page,
209+
pane,
210+
resizedSize.width - paneCase.size.width,
211+
resizedSize.height - paneCase.size.height
212+
);
213+
await expectPaneSize(pane, resizedSize);
214+
});
215+
216+
await test.step('Resize Pane Reset', async () => {
217+
const pane = firstPane(page);
218+
await pane.locator('.react-resizable-handle').first().dblclick();
219+
await expectPaneSize(pane, paneCase.resetSize);
220+
});
221+
222+
await test.step('Close Pane', async () => {
223+
await firstPane(page).locator('button[title="close"]').click();
224+
await expect(page.locator(winSelector)).toHaveCount(3);
225+
});
226+
});
227+
}
228+
});
229+
230+
test.describe('Test Pane Filter', () => {
231+
test.beforeEach(async ({ page }) => {
232+
await page.goto('/');
233+
});
234+
235+
test('filters panes by plain text and regex', async ({ page }) => {
236+
const env = `pane_basic_${Math.floor(Math.random() * 1e6)}`;
237+
const filter = page.locator('[data-cy="filter"]');
238+
const visibleWindows = page.locator(`${windowSelector}:visible`);
239+
240+
await runDemo(page, 'text_basic', {
241+
env: env,
242+
open: false,
243+
args: ['"pane1 tag1"'],
244+
});
245+
await runDemo(page, 'text_basic', {
246+
env: env,
247+
open: false,
248+
args: ['"pane2 tag1 tag2"'],
249+
});
250+
await runDemo(page, 'text_basic', {
251+
env: env,
252+
open: false,
253+
args: ['"pane3 tag2"'],
254+
});
255+
await runDemo(page, 'text_basic', {
256+
env: env,
257+
args: ['"pane4 tag2"'],
258+
});
259+
await expect(page.locator(windowSelector)).toHaveCount(4);
260+
261+
await filter.fill('tag1', { force: true });
262+
await expect(visibleWindows).toHaveCount(2);
263+
264+
await filter.fill('tag2', { force: true });
265+
await expect(visibleWindows).toHaveCount(3);
266+
267+
await filter.fill('pane3', { force: true });
268+
await expect(visibleWindows).toHaveCount(1);
269+
270+
await filter.fill('pane3|pane2', { force: true });
271+
await expect(visibleWindows).toHaveCount(2);
272+
});
273+
});

0 commit comments

Comments
 (0)