chart/tests/e2e/drawings.test.mjs

144 lines
7.9 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
withChart, chartBox, at, armTool, drawingIds, assertNoPageErrors,
} from './helpers.mjs';
test('editing a drawing name with Backspace or Delete cannot delete the drawing',
{ timeout: 180000 }, async () => {
await withChart(async page => {
const label = `e2e rename ${Date.now()}`;
const box = await chartBox(page);
await armTool(page, 'Trendline');
assert.deepEqual(await page.locator('select[aria-label="Trendline width"] option').allTextContents(),
['1px', '2px', '3px', '4px', '5px', '6px', '7px', '8px', '9px']);
await page.selectOption('select[aria-label="Trendline width"]', '9');
await page.fill('input[aria-label="Trendline label"]', label);
const first = at(box, 0.42, 0.55);
const second = at(box, 0.62, 0.40);
await page.mouse.click(first.x, first.y);
await page.waitForTimeout(300);
await page.mouse.click(second.x, second.y);
await page.waitForFunction(name =>
[...document.querySelectorAll('input[aria-label="Drawing name"]')]
.some(input => input.value === name), label);
await page.waitForFunction(name =>
window.__chart.levels.some(level => level.label === name && !level.id.startsWith('tmp_')),
label);
const created = await page.evaluate(name => {
const line = window.__chart.levels.find(
level => level.label === name && !level.id.startsWith('tmp_'),
);
return line?.id || null;
}, label);
assert.ok(created, 'the trendline was not created');
assert.ok((await drawingIds(page)).includes(created), 'the trendline was not persisted');
assert.equal(await page.evaluate(id =>
window.__chart.levels.find(level => level.id === id)?.line_width, created), 9,
'the 9px trendline width was not persisted');
await page.fill('input[aria-label="Filter drawings by text"]', label);
const input = page.locator('input[aria-label="Drawing name"]');
assert.equal(await input.count(), 1, 'the unique drawing filter did not isolate the test line');
const row = page.locator('.trendline-row');
assert.deepEqual(await row.locator('select[aria-label="Drawing width"] option').allTextContents(),
['1', '2', '3', '4', '5', '6', '7', '8', '9']);
const rowBox = await row.boundingBox();
assert.ok(rowBox.height <= 48, `drawing row is still ${rowBox.height}px tall`);
assert.equal(await row.locator('.drawing-state .fa-bell').count(), 1,
'the armed state is not represented by a bell');
assert.equal(await row.locator('.color-picker>summary').getAttribute('title'), 'Cyan',
'the current-color swatch does not name its color');
const checkbox = row.locator('.line-select');
if (await checkbox.isChecked()) await checkbox.click();
await row.locator('.drawing-secondary>span').click();
await page.waitForFunction(id => window.__chart.selectedLineId === id, created);
assert.equal(await page.locator('.chart-line-focus').isVisible(), true,
'selecting the drawing did not point out its trendline on the chart');
await row.locator('.color-picker>summary').click();
assert.equal(await row.locator('.color-popover>button[aria-label^="Use "]').count(), 16,
'the preset palette does not contain 16 colors');
assert.equal(await row.locator('.color-popover>button[title="Cyan"]').count(), 1,
'palette colors do not expose readable names');
assert.equal(await row.locator('.custom-color input[type="color"]').count(), 1,
'the custom color choice is missing');
assert.equal(await row.locator('.palette-close').count(), 1,
'the palette close control is missing');
await page.keyboard.press('Escape');
assert.equal(await row.locator('.color-popover').isVisible(), false,
'Escape did not close the color palette');
await row.locator('.color-picker>summary').click();
await row.locator('.palette-close').click();
assert.equal(await row.locator('.color-popover').isVisible(), false,
'the close control did not close the color palette');
await row.locator('.color-picker>summary').click();
await row.locator('button[aria-label="Use Red (#bd4545)"]').click();
await page.waitForFunction(([id, color]) =>
window.__chart.levels.find(level => level.id === id)?.color === color,
[created, '#bd4545']);
await input.focus();
await input.press('End');
await input.press('Backspace');
assert.equal(await input.inputValue(), label.slice(0, -1), 'Backspace did not edit the name');
await page.waitForTimeout(500);
assert.ok((await drawingIds(page)).includes(created), 'Backspace deleted the drawing');
const beforeDelete = await input.inputValue();
await input.press('Home');
await input.press('Delete');
assert.equal(await input.inputValue(), beforeDelete.slice(1), 'Delete did not edit the name');
await page.waitForTimeout(500);
assert.ok((await drawingIds(page)).includes(created), 'Delete deleted the drawing');
assertNoPageErrors(page, assert);
});
});
test('a price level selects and focuses in both directions', { timeout: 180000 }, async () => {
await withChart(async page => {
const label = `e2e level focus ${Date.now()}`;
const price = await page.evaluate(() => window.__chart.bars.at(-1).c);
await armTool(page, 'Price level');
assert.deepEqual(await page.locator('select[aria-label="Level width"] option').allTextContents(),
['1px', '2px', '3px', '4px', '5px', '6px', '7px', '8px', '9px']);
await page.fill('input[aria-label="Level label"]', label);
await page.fill('input[aria-label="Level price"]', String(price));
await page.locator('.price-row button[type="submit"]').click();
await page.fill('input[aria-label="Filter drawings by text"]', label);
const row = page.locator('.trendline-row');
await row.waitFor();
const id = await page.evaluate(name =>
window.__chart.levels.find(level => level.label === name)?.id || null, label);
assert.ok(id, 'the price level was not created');
const number = await page.evaluate(levelId =>
window.__chart.levels.find(level => level.id === levelId).number, id);
assert.equal(await page.evaluate(levelId =>
window.__chart.priceLines.get(levelId).line.options().title, id), `#${number}`,
'an unselected price level exposed its full name on the price axis');
await row.locator('.drawing-secondary>span').click();
await page.waitForFunction(levelId => window.__chart.selectedLineId === levelId, id);
assert.equal(await page.evaluate(levelId =>
window.__chart.priceLines.get(levelId).line.options().title, id), label,
'a selected price level did not show its full name on the price axis');
const focus = page.locator('.chart-line-focus');
assert.equal(await focus.getAttribute('hidden'), null, 'the selected price level was not highlighted');
assert.equal(await focus.evaluate(node => getComputedStyle(node).animationName), 'line-focus',
'the price-level focus animation did not run');
assert.equal(await focus.getAttribute('y1'), await focus.getAttribute('y2'),
'the price-level highlight was not horizontal');
await page.keyboard.press('Escape');
const point = await page.evaluate(levelId => {
const chart = window.__chart;
const level = chart.levels.find(value => value.id === levelId);
const plot = chart.plotCanvas().getBoundingClientRect();
return { x: plot.left + plot.width / 2, y: plot.top + chart.candles.priceToCoordinate(level.anchor_p) };
}, id);
await page.mouse.click(point.x, point.y);
await page.waitForFunction(levelId => window.__chart.selectedLineId === levelId, id);
assert.equal(await row.evaluate(node => node.classList.contains('selected')), true,
'clicking the chart price level did not select its drawing row');
assertNoPageErrors(page, assert);
});
});