feat(web-test): add navigateLink() for direct 1C navigation links

- navigateLink(url): opens form via Shift+F11 dialog with clipboard paste
- Grant clipboard-read/write permissions on browser context creation
- Register navigateLink in ACTION_FNS for auto-error detection
- Document in SKILL.md with examples (e1cib/list/...)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-02-28 15:37:58 +03:00
parent 0fbdd298ca
commit e58f5c1f82
3 changed files with 46 additions and 3 deletions
+12
View File
@@ -98,6 +98,7 @@ In `exec` sandbox, all browser.mjs functions are available as globals — no `im
|----------|-------------|
| `navigateSection(name)` | Go to section (fuzzy match). Returns `{ sections, commands }` |
| `openCommand(name)` | Open command from function panel (fuzzy). Returns form state |
| `navigateLink(url)` | Open 1C navigation link via Shift+F11 dialog. Returns form state |
| `switchTab(name)` | Switch to open tab/window (fuzzy). Returns form state |
### Reading
@@ -214,6 +215,17 @@ Hint: if `readTable()` returns `hierarchical: true`, the list has groups.
| `F4` | Reference field focused | Open selection form |
| `Alt+F` | List/table form | Open advanced search dialog |
### Navigation links
```js
// Open any form directly by 1C navigation link (e1cib/...)
await navigateLink('e1cib/list/РегистрНакопления.ЗаказыКлиентов');
await navigateLink('e1cib/list/Документ.ЗаказКлиента');
await navigateLink('e1cib/list/Справочник.Контрагенты');
```
Bypasses section/command navigation. Useful for registers, journals, and any form with a known path.
### Submenu navigation
```js
+32 -1
View File
@@ -40,7 +40,10 @@ export async function connect(url) {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: LOAD_TIMEOUT });
} else {
browser = await chromium.launch({ headless: false, args: ['--start-maximized'] });
const context = await browser.newContext({ viewport: null });
const context = await browser.newContext({
viewport: null,
permissions: ['clipboard-read', 'clipboard-write'],
});
page = await context.newPage();
// Capture seanceId from network requests for graceful logout
@@ -337,6 +340,34 @@ export async function switchTab(name) {
return await getFormState();
}
/** Navigate to a 1C navigation link via Shift+F11 dialog. Returns new form state. */
export async function navigateLink(url) {
ensureConnected();
await dismissPendingErrors();
const formBefore = await page.evaluate(detectFormScript());
// Copy link to clipboard, press Shift+F11 (opens "Go to link" dialog with clipboard content)
await page.evaluate(`navigator.clipboard.writeText(${JSON.stringify(url)})`);
await page.keyboard.press('Shift+F11');
await waitForStable();
// Click "Перейти" in the navigation dialog
const dialog = await page.evaluate(detectFormScript());
if (dialog != null && dialog !== formBefore) {
const btns = await page.$$(`#form${dialog}_container a.press`);
for (const b of btns) {
const txt = (await b.textContent())?.trim();
if (txt === 'Перейти') { await b.click(); break; }
}
}
await waitForStable(formBefore);
const state = await getFormState();
const err = await checkForErrors();
if (err) state.errors = err;
return state;
}
/** Read current form state. Single evaluate call via combined script. */
export async function getFormState() {
ensureConnected();
+2 -2
View File
@@ -117,8 +117,8 @@ async function executeScript(code) {
// and stop execution immediately with diagnostic info
const ACTION_FNS = [
'clickElement', 'fillFields', 'selectValue', 'fillTableRow',
'deleteTableRow', 'openCommand', 'navigateSection', 'closeForm',
'filterList', 'unfilterList'
'deleteTableRow', 'openCommand', 'navigateSection', 'navigateLink',
'closeForm', 'filterList', 'unfilterList'
];
for (const name of ACTION_FNS) {
if (typeof exports[name] !== 'function') continue;