codeceptjs 4.0.0-rc.10 → 4.0.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/mcp-server.js
CHANGED
|
@@ -12,7 +12,8 @@ import path from 'path'
|
|
|
12
12
|
import crypto from 'crypto'
|
|
13
13
|
import { spawn } from 'child_process'
|
|
14
14
|
import { createRequire } from 'module'
|
|
15
|
-
import { existsSync, readdirSync } from 'fs'
|
|
15
|
+
import { existsSync, readdirSync, writeFileSync } from 'fs'
|
|
16
|
+
import { mkdirp } from 'mkdirp'
|
|
16
17
|
|
|
17
18
|
const require = createRequire(import.meta.url)
|
|
18
19
|
|
|
@@ -439,12 +440,38 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
439
440
|
const helper = Object.values(helpers)[0]
|
|
440
441
|
if (helper) {
|
|
441
442
|
try {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
443
|
+
const traceDir = getTraceDir('mcp', 'run_code')
|
|
444
|
+
mkdirp.sync(traceDir)
|
|
445
|
+
|
|
446
|
+
if (helper.grabAriaSnapshot) {
|
|
447
|
+
const aria = await helper.grabAriaSnapshot()
|
|
448
|
+
const ariaFile = path.join(traceDir, 'aria.txt')
|
|
449
|
+
writeFileSync(ariaFile, aria)
|
|
450
|
+
result.artifacts.aria = `file://${ariaFile}`
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (helper.grabCurrentUrl) {
|
|
454
|
+
result.artifacts.url = await helper.grabCurrentUrl()
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (helper.grabBrowserLogs) {
|
|
458
|
+
const logs = (await helper.grabBrowserLogs()) || []
|
|
459
|
+
const logsFile = path.join(traceDir, 'console.json')
|
|
460
|
+
writeFileSync(logsFile, JSON.stringify(logs, null, 2))
|
|
461
|
+
result.artifacts.consoleLogs = `file://${logsFile}`
|
|
462
|
+
}
|
|
463
|
+
|
|
445
464
|
if (helper.grabSource) {
|
|
446
465
|
const html = await helper.grabSource()
|
|
447
|
-
|
|
466
|
+
const htmlFile = path.join(traceDir, 'page.html')
|
|
467
|
+
writeFileSync(htmlFile, html)
|
|
468
|
+
result.artifacts.html = `file://${htmlFile}`
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (helper.saveScreenshot) {
|
|
472
|
+
const screenshotFile = path.join(traceDir, 'screenshot.png')
|
|
473
|
+
await helper.saveScreenshot(screenshotFile)
|
|
474
|
+
result.artifacts.screenshot = `file://${screenshotFile}`
|
|
448
475
|
}
|
|
449
476
|
} catch (e) {
|
|
450
477
|
result.output += ` (Warning: ${e.message})`
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -7,6 +7,7 @@ import promiseRetry from 'promise-retry'
|
|
|
7
7
|
import Locator from '../locator.js'
|
|
8
8
|
import recorder from '../recorder.js'
|
|
9
9
|
import store from '../store.js'
|
|
10
|
+
import { checkFocusBeforeType, checkFocusBeforePressKey } from './extras/focusCheck.js'
|
|
10
11
|
import { includes as stringIncludes } from '../assert/include.js'
|
|
11
12
|
import { urlEquals, equals } from '../assert/equal.js'
|
|
12
13
|
import { empty } from '../assert/empty.js'
|
|
@@ -2231,6 +2232,7 @@ class Playwright extends Helper {
|
|
|
2231
2232
|
* {{> pressKeyWithKeyNormalization }}
|
|
2232
2233
|
*/
|
|
2233
2234
|
async pressKey(key) {
|
|
2235
|
+
await checkFocusBeforePressKey(this, key)
|
|
2234
2236
|
const modifiers = []
|
|
2235
2237
|
if (Array.isArray(key)) {
|
|
2236
2238
|
for (let k of key) {
|
|
@@ -2259,6 +2261,8 @@ class Playwright extends Helper {
|
|
|
2259
2261
|
* {{> type }}
|
|
2260
2262
|
*/
|
|
2261
2263
|
async type(keys, delay = null) {
|
|
2264
|
+
await checkFocusBeforeType(this)
|
|
2265
|
+
|
|
2262
2266
|
// Always use page.keyboard.type for any string (including single character and national characters).
|
|
2263
2267
|
if (!Array.isArray(keys)) {
|
|
2264
2268
|
keys = keys.toString()
|
|
@@ -2933,7 +2937,7 @@ class Playwright extends Helper {
|
|
|
2933
2937
|
const els = await this._locate(matchedLocator)
|
|
2934
2938
|
assertElementExists(els, locator)
|
|
2935
2939
|
const snapshot = await els[0].ariaSnapshot()
|
|
2936
|
-
this.debugSection('Aria Snapshot', snapshot)
|
|
2940
|
+
this.debugSection('Aria Snapshot', `${snapshot.split('\n').length} lines`)
|
|
2937
2941
|
return snapshot
|
|
2938
2942
|
}
|
|
2939
2943
|
|
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -8,6 +8,7 @@ import promiseRetry from 'promise-retry'
|
|
|
8
8
|
import Locator from '../locator.js'
|
|
9
9
|
import recorder from '../recorder.js'
|
|
10
10
|
import store from '../store.js'
|
|
11
|
+
import { checkFocusBeforeType, checkFocusBeforePressKey } from './extras/focusCheck.js'
|
|
11
12
|
import { includes as stringIncludes } from '../assert/include.js'
|
|
12
13
|
import { urlEquals, equals } from '../assert/equal.js'
|
|
13
14
|
import { empty } from '../assert/empty.js'
|
|
@@ -1547,6 +1548,7 @@ class Puppeteer extends Helper {
|
|
|
1547
1548
|
* {{> pressKeyWithKeyNormalization }}
|
|
1548
1549
|
*/
|
|
1549
1550
|
async pressKey(key) {
|
|
1551
|
+
await checkFocusBeforePressKey(this, key)
|
|
1550
1552
|
const modifiers = []
|
|
1551
1553
|
if (Array.isArray(key)) {
|
|
1552
1554
|
for (let k of key) {
|
|
@@ -1575,6 +1577,8 @@ class Puppeteer extends Helper {
|
|
|
1575
1577
|
* {{> type }}
|
|
1576
1578
|
*/
|
|
1577
1579
|
async type(keys, delay = null) {
|
|
1580
|
+
await checkFocusBeforeType(this)
|
|
1581
|
+
|
|
1578
1582
|
if (!Array.isArray(keys)) {
|
|
1579
1583
|
keys = keys.toString()
|
|
1580
1584
|
keys = keys.split('')
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -10,6 +10,7 @@ import promiseRetry from 'promise-retry'
|
|
|
10
10
|
import { includes as stringIncludes } from '../assert/include.js'
|
|
11
11
|
import { urlEquals, equals } from '../assert/equal.js'
|
|
12
12
|
import store from '../store.js'
|
|
13
|
+
import { checkFocusBeforeType, checkFocusBeforePressKey } from './extras/focusCheck.js'
|
|
13
14
|
import output from '../output.js'
|
|
14
15
|
const { debug } = output
|
|
15
16
|
import { empty } from '../assert/empty.js'
|
|
@@ -2237,6 +2238,7 @@ class WebDriver extends Helper {
|
|
|
2237
2238
|
* {{> pressKeyWithKeyNormalization }}
|
|
2238
2239
|
*/
|
|
2239
2240
|
async pressKey(key) {
|
|
2241
|
+
await checkFocusBeforePressKey(this, key)
|
|
2240
2242
|
const modifiers = []
|
|
2241
2243
|
if (Array.isArray(key)) {
|
|
2242
2244
|
for (let k of key) {
|
|
@@ -2283,6 +2285,8 @@ class WebDriver extends Helper {
|
|
|
2283
2285
|
* {{> type }}
|
|
2284
2286
|
*/
|
|
2285
2287
|
async type(keys, delay = null) {
|
|
2288
|
+
await checkFocusBeforeType(this)
|
|
2289
|
+
|
|
2286
2290
|
if (!Array.isArray(keys)) {
|
|
2287
2291
|
keys = keys.toString()
|
|
2288
2292
|
keys = keys.split('')
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import store from '../../store.js'
|
|
2
|
+
import NonFocusedType from '../errors/NonFocusedType.js'
|
|
3
|
+
|
|
4
|
+
const MODIFIER_PATTERN = /^(control|ctrl|meta|cmd|command|commandorcontrol|ctrlorcommand)/i
|
|
5
|
+
const EDITING_KEYS = new Set(['a', 'c', 'x', 'v', 'z', 'y'])
|
|
6
|
+
|
|
7
|
+
async function isNoElementFocused(helper) {
|
|
8
|
+
return helper.executeScript(() => {
|
|
9
|
+
const ae = document.activeElement
|
|
10
|
+
return !ae || ae === document.documentElement || (ae === document.body && !ae.isContentEditable)
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function checkFocusBeforeType(helper) {
|
|
15
|
+
if (!helper.options.strict && !store.debugMode) return
|
|
16
|
+
if (!await isNoElementFocused(helper)) return
|
|
17
|
+
|
|
18
|
+
const message = 'No element is in focus. Use I.click() or I.focus() to activate an element before typing.'
|
|
19
|
+
if (helper.options.strict) throw new NonFocusedType(message)
|
|
20
|
+
helper.debugSection('Warning', message)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function checkFocusBeforePressKey(helper, originalKey) {
|
|
24
|
+
if (!helper.options.strict && !store.debugMode) return
|
|
25
|
+
if (!Array.isArray(originalKey)) return
|
|
26
|
+
|
|
27
|
+
let hasCtrlOrMeta = false
|
|
28
|
+
let actionKey = null
|
|
29
|
+
for (const k of originalKey) {
|
|
30
|
+
if (MODIFIER_PATTERN.test(k)) {
|
|
31
|
+
hasCtrlOrMeta = true
|
|
32
|
+
} else {
|
|
33
|
+
actionKey = k
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (!hasCtrlOrMeta || !actionKey || !EDITING_KEYS.has(actionKey.toLowerCase())) return
|
|
37
|
+
|
|
38
|
+
if (!await isNoElementFocused(helper)) return
|
|
39
|
+
|
|
40
|
+
const message = `No element is in focus. Key combination with "${originalKey.join('+')}" may not work as expected. Use I.click() or I.focus() first.`
|
|
41
|
+
if (helper.options.strict) throw new NonFocusedType(message)
|
|
42
|
+
helper.debugSection('Warning', message)
|
|
43
|
+
}
|