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'); 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'); 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'); 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'); 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); }); });