mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-16 02:43:14 +03:00
fix(web-test): deleteTableRow выходит из cell edit-mode перед Delete
Delete-клавиша в режиме редактирования ячейки очищает буфер ввода, а не удаляет строку. Это становилось проблемой когда: 1. предыдущий fillTableRow закончил Tab-навигацией в input (например в Number-ячейку соседней колонки), и фокус остался там; 2. сам click на Number/Date ячейку в deleteTableRow автоматически входит в edit-mode (поведение 1С). Фикс: в deleteTableRow проверяем isInputFocusedInGrid дважды — до и после click — и шлём Escape если активен INPUT в целевом гриде. Строка остаётся выделенной после Escape, Delete срабатывает. Дополнительно: isInputFocusedInGridScript / isInputFocusedInGrid теперь принимают опциональный gridSelector — чтобы можно было прицельно проверять конкретный грид на многогрид-формах (а не любой `.grid` на странице). Покрытие: новый шаг в 05-table проверяет сценарий «фокус снаружи грида (Комментарий), потом delete» — гарантирует что post-click Escape ловит автоматический вход в edit-mode при клике на Number-ячейку. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// web-test dom/edit-state v1.0 — focus and popup detection inside the 1C web client
|
||||
// web-test dom/edit-state v1.1 — focus and popup detection inside the 1C web client
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
/**
|
||||
@@ -21,12 +21,22 @@ export function isInputFocusedScript({ allowTextarea = false } = {}) {
|
||||
/**
|
||||
* Is the currently focused INPUT/TEXTAREA inside a `.grid` ancestor?
|
||||
* Used to verify grid edit-mode (active cell editor).
|
||||
*
|
||||
* @param {string} [gridSelector] — when given, only `true` if the focused input
|
||||
* is inside that specific grid. Without it — any `.grid` ancestor counts.
|
||||
*
|
||||
* Returns boolean.
|
||||
*/
|
||||
export function isInputFocusedInGridScript() {
|
||||
export function isInputFocusedInGridScript(gridSelector) {
|
||||
const sel = gridSelector ? JSON.stringify(gridSelector) : 'null';
|
||||
return `(() => {
|
||||
const f = document.activeElement;
|
||||
if (!f || (f.tagName !== 'INPUT' && f.tagName !== 'TEXTAREA')) return false;
|
||||
const sel = ${sel};
|
||||
if (sel) {
|
||||
const grid = document.querySelector(sel);
|
||||
return !!(grid && grid.contains(f));
|
||||
}
|
||||
let n = f;
|
||||
while (n) {
|
||||
if (n.classList?.contains('grid')) return true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// web-test core/helpers v1.20 — private, cross-cutting helpers used by the
|
||||
// web-test core/helpers v1.21 — private, cross-cutting helpers used by the
|
||||
// public action functions (clickElement/fillFields/selectValue/etc).
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
@@ -94,10 +94,11 @@ export async function isInputFocused({ allowTextarea = false } = {}) {
|
||||
|
||||
/**
|
||||
* Thin wrapper: is the currently focused INPUT/TEXTAREA inside a `.grid`?
|
||||
* Used to verify grid edit-mode.
|
||||
* Used to verify grid edit-mode. Pass `{ gridSelector }` to scope the check
|
||||
* to a specific grid (when a form has multiple grids).
|
||||
*/
|
||||
export async function isInputFocusedInGrid() {
|
||||
return page.evaluate(isInputFocusedInGridScript());
|
||||
export async function isInputFocusedInGrid({ gridSelector } = {}) {
|
||||
return page.evaluate(isInputFocusedInGridScript(gridSelector));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// web-test table/grid v1.19 — Form-grid operations: read table rows, fill rows, delete rows.
|
||||
// web-test table/grid v1.20 — Form-grid operations: read table rows, fill rows, delete rows.
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
//
|
||||
// "Grid" в терминах 1С — таблица на форме (.gridLine/.gridBody/.grid в DOM):
|
||||
@@ -8,6 +8,7 @@
|
||||
import { page, ensureConnected } from '../core/state.mjs';
|
||||
import { detectFormScript, readTableScript, resolveGridScript } from '../../dom.mjs';
|
||||
import { findDeleteRowCoordsScript, countGridRowsScript } from '../../dom/grid.mjs';
|
||||
import { isInputFocusedInGrid } from '../core/helpers.mjs';
|
||||
import { dismissPendingErrors } from '../core/errors.mjs';
|
||||
import { waitForStable } from '../core/wait.mjs';
|
||||
import { clickElement } from '../core/click.mjs';
|
||||
@@ -63,10 +64,26 @@ export async function deleteTableRow(row, { tab, table } = {}) {
|
||||
|
||||
const rowsBefore = cellCoords.total;
|
||||
|
||||
// Pre-click Escape: leftover edit-mode from a prior fillTableRow Tab-navigation.
|
||||
// Without it the next mouse click may not select the row reliably (the active
|
||||
// edit input intercepts the event timing).
|
||||
if (await isInputFocusedInGrid({ gridSelector })) {
|
||||
await page.keyboard.press('Escape');
|
||||
await page.waitForTimeout(150);
|
||||
}
|
||||
|
||||
// Single click to select the row
|
||||
await page.mouse.click(cellCoords.x, cellCoords.y);
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Post-click Escape: clicking a Number/Date cell auto-enters edit mode in 1С.
|
||||
// Delete in edit mode clears the cell buffer instead of deleting the row, so
|
||||
// we exit edit first. The row remains selected after Escape — Delete acts on it.
|
||||
if (await isInputFocusedInGrid({ gridSelector })) {
|
||||
await page.keyboard.press('Escape');
|
||||
await page.waitForTimeout(150);
|
||||
}
|
||||
|
||||
// 3. Press Delete to remove the row
|
||||
await page.keyboard.press('Delete');
|
||||
await waitForStable();
|
||||
|
||||
@@ -90,6 +90,20 @@ export default async function({ navigateSection, openCommand, clickElement, fill
|
||||
log(`rows after delete: ${t.rows?.length}, [0]=${t.rows[0]?.['Номенклатура']}`);
|
||||
assert.equal(t.rows?.length, 1, 'Должна остаться 1 строка');
|
||||
assert.equal(t.rows[0]['Номенклатура'], 'Товар 02', 'Осталась строка Товар 02');
|
||||
});
|
||||
|
||||
await step('delete: фокус вне грида (Комментарий) — delete всё равно должен работать', async () => {
|
||||
// Воспроизводит сценарий, когда последнее действие было НЕ в табчасти.
|
||||
// deleteTableRow должен корректно перехватить фокус и удалить строку
|
||||
// несмотря на то, что click на Number-ячейку входит в edit-mode (post-click Escape).
|
||||
await fillTableRow({ 'Номенклатура': 'Товар 03', 'Количество': '8' }, { table: 'Товары', add: true });
|
||||
// Перевести фокус на Комментарий (вне грида).
|
||||
await fillFields({ 'Комментарий': 'focus-outside-grid' });
|
||||
await deleteTableRow(0, { table: 'Товары' });
|
||||
const t = await readTable({ table: 'Товары' });
|
||||
log(`rows after delete: ${t.rows?.length}, names=${t.rows.map(r => r['Номенклатура']).join(',')}`);
|
||||
assert.equal(t.rows?.length, 1, 'Должна остаться 1 строка');
|
||||
assert.equal(t.rows[0]['Номенклатура'], 'Товар 03', 'Удалена первая (Товар 02), осталась Товар 03');
|
||||
await closeForm({ save: false });
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user