@epoint-testtech/stage-core 0.0.3-alpha.1
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/README.md +40 -0
- package/dist/assets/content-scripts/autofill/autofill-bridge.js +86 -0
- package/dist/assets/content-scripts/autofill/autofill-button-playwright.js +120 -0
- package/dist/assets/content-scripts/autofill/autofill-fill.js +1344 -0
- package/dist/assets/content-scripts/autofill/mock-min.js +6 -0
- package/dist/core/autofill-injector.d.ts +69 -0
- package/dist/core/autofill-injector.d.ts.map +1 -0
- package/dist/core/autofill-injector.js +373 -0
- package/dist/core/constants.d.ts +16 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +25 -0
- package/dist/core/stage-api.d.ts +63 -0
- package/dist/core/stage-api.d.ts.map +1 -0
- package/dist/core/stage-api.js +154 -0
- package/dist/core/stage-context.d.ts +15 -0
- package/dist/core/stage-context.d.ts.map +1 -0
- package/dist/core/stage-context.js +40 -0
- package/dist/core/stage-flow.d.ts +65 -0
- package/dist/core/stage-flow.d.ts.map +1 -0
- package/dist/core/stage-flow.js +114 -0
- package/dist/core/stage-locator.d.ts +27 -0
- package/dist/core/stage-locator.d.ts.map +1 -0
- package/dist/core/stage-locator.js +63 -0
- package/dist/core/stage-path.d.ts +61 -0
- package/dist/core/stage-path.d.ts.map +1 -0
- package/dist/core/stage-path.js +119 -0
- package/dist/core/stage-report.d.ts +59 -0
- package/dist/core/stage-report.d.ts.map +1 -0
- package/dist/core/stage-report.js +174 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/reporter/index.d.ts +24 -0
- package/dist/reporter/index.d.ts.map +1 -0
- package/dist/reporter/index.js +32 -0
- package/dist/utils/env-util.d.ts +20 -0
- package/dist/utils/env-util.d.ts.map +1 -0
- package/dist/utils/env-util.js +27 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/log-util.d.ts +20 -0
- package/dist/utils/log-util.d.ts.map +1 -0
- package/dist/utils/log-util.js +184 -0
- package/dist/utils/time-stamp-util.d.ts +17 -0
- package/dist/utils/time-stamp-util.d.ts.map +1 -0
- package/dist/utils/time-stamp-util.js +27 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @epoint/stage-core
|
|
2
|
+
|
|
3
|
+
> Epoint Stage — Playwright 测试框架核心库
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @epoint/stage-core
|
|
9
|
+
# 或
|
|
10
|
+
pnpm add @epoint/stage-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 使用
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { StageContext, StagePath, stageLocator, syncAutoFill } from '@epoint/stage-core';
|
|
17
|
+
import { log } from '@epoint/stage-core/utils';
|
|
18
|
+
|
|
19
|
+
StageContext.init();
|
|
20
|
+
await syncAutoFill(page);
|
|
21
|
+
log.info(StagePath.authFilePath);
|
|
22
|
+
const locator = stageLocator(page.locator('xpath=//button[@id="submit"]'), '提交按钮');
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 核心模块
|
|
26
|
+
|
|
27
|
+
- **StageContext** - 测试上下文管理
|
|
28
|
+
- **StagePath** - 页面路径管理
|
|
29
|
+
- **stageLocator** - 元素定位器(优先使用 xpath)
|
|
30
|
+
- **syncAutoFill** - 基于前端框架的表单自动填充能力
|
|
31
|
+
|
|
32
|
+
## 开发
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# 安装依赖
|
|
36
|
+
pnpm install
|
|
37
|
+
|
|
38
|
+
# 构建
|
|
39
|
+
pnpm build
|
|
40
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autofill-bridge.js — 桥接脚本
|
|
3
|
+
*
|
|
4
|
+
* - 监听 CustomEvent 触发填充
|
|
5
|
+
* - 定时探测可填充控件,通过 DOM 属性通知按钮显隐
|
|
6
|
+
* - 监听快捷键触发填充
|
|
7
|
+
* - 每个 frame 的 mini.findControls() 仅查找当前 frame 的 MiniUI 控件
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
(function () {
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
function doFill(fillType, api) {
|
|
14
|
+
if (fillType === 'required') {
|
|
15
|
+
api.requiredFill();
|
|
16
|
+
} else {
|
|
17
|
+
api.defaultFill();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var themeReg = /^https?:\/\/.*\/fui\/pages\/themes\/(\w+)\/\1/i;
|
|
22
|
+
|
|
23
|
+
function updateVisibility() {
|
|
24
|
+
// 主题页不显示填充按钮(无表单控件可填)
|
|
25
|
+
if (themeReg.test(window.location.toString())) {
|
|
26
|
+
document.body.setAttribute('data-epoint-autofill', '0');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
var api = window.__epoint_autoFill;
|
|
30
|
+
var has = !!(api && api.hasFillableControls && api.hasFillableControls());
|
|
31
|
+
document.body.setAttribute('data-epoint-autofill', has ? '1' : '0');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 首次探测:MiniUI 控件可能在脚本注入后才渲染
|
|
35
|
+
setTimeout(updateVisibility, 300);
|
|
36
|
+
|
|
37
|
+
// 定时刷新
|
|
38
|
+
var _visInterval = setInterval(updateVisibility, 1000);
|
|
39
|
+
|
|
40
|
+
// MiniUI 框架初始化完成后再次探测(epoint 框架特有事件)
|
|
41
|
+
if (window.epoint && window.epoint.on) {
|
|
42
|
+
window.epoint.on('afterInit', function () {
|
|
43
|
+
setTimeout(updateVisibility, 500);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 监听清理(停止定时器)
|
|
48
|
+
document.addEventListener('epoint:remove-autofill-btn', function () {
|
|
49
|
+
clearInterval(_visInterval);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ── 快捷键监听 ──
|
|
53
|
+
if (!window.__epoint_autofill_hotkey_bound__) {
|
|
54
|
+
window.__epoint_autofill_hotkey_bound__ = true;
|
|
55
|
+
document.addEventListener('keydown', function (event) {
|
|
56
|
+
var key = event.key;
|
|
57
|
+
var api = window.__epoint_autoFill;
|
|
58
|
+
if (!api) return;
|
|
59
|
+
|
|
60
|
+
// Ctrl+Alt+Shift+A → 默认填充(所有可填充控件)
|
|
61
|
+
if (event.ctrlKey && event.altKey && event.shiftKey && (key === 'A' || key === 'a')) {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
try { api.defaultFill(); } catch (e) { console.error('[auto-fill] 快捷键默认填充失败:', e); }
|
|
64
|
+
// Shift+A(无 Ctrl/Alt)→ 仅填充必填项
|
|
65
|
+
} else if (!event.ctrlKey && !event.altKey && event.shiftKey && (key === 'A' || key === 'a')) {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
try { api.requiredFill(); } catch (e) { console.error('[auto-fill] 快捷键必填填充失败:', e); }
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 监听填充触发
|
|
73
|
+
document.addEventListener('__epoint_autofill_trigger__', function (e) {
|
|
74
|
+
if (!e.detail || !e.detail.fillType) return;
|
|
75
|
+
var fillType = e.detail.fillType;
|
|
76
|
+
|
|
77
|
+
var api = window.__epoint_autoFill;
|
|
78
|
+
if (!api) return;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
doFill(fillType, api);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error('[auto-fill] 填充失败:', err);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
})();
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autofill-button-playwright.js — 页面悬浮填充工具栏
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
(function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
if (document.getElementById('__epoint_autofill_btn__')) return;
|
|
9
|
+
|
|
10
|
+
var bar = document.createElement('div');
|
|
11
|
+
bar.id = '__epoint_autofill_btn__';
|
|
12
|
+
bar.style.display = 'none'; // 默认隐藏,bridge 探测到可填充控件后显示
|
|
13
|
+
bar.innerHTML =
|
|
14
|
+
'<button class="af-btn af-btn-fill" data-fill="default" title="默认填充">' +
|
|
15
|
+
'<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' +
|
|
16
|
+
'<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>' +
|
|
17
|
+
'</svg>' +
|
|
18
|
+
'<span>默认填充</span>' +
|
|
19
|
+
'</button>' +
|
|
20
|
+
'<button class="af-btn af-btn-req" data-fill="required" title="仅必填">' +
|
|
21
|
+
'<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' +
|
|
22
|
+
'<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>' +
|
|
23
|
+
'<polyline points="22 4 12 14.01 9 11.01"/>' +
|
|
24
|
+
'</svg>' +
|
|
25
|
+
'<span>仅必填</span>' +
|
|
26
|
+
'</button>' +
|
|
27
|
+
'<div class="af-handle" title="拖拽移动">' +
|
|
28
|
+
'<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" opacity="0.35">' +
|
|
29
|
+
'<circle cx="9" cy="5" r="1.5"/><circle cx="15" cy="5" r="1.5"/>' +
|
|
30
|
+
'<circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/>' +
|
|
31
|
+
'<circle cx="9" cy="19" r="1.5"/><circle cx="15" cy="19" r="1.5"/>' +
|
|
32
|
+
'</svg>' +
|
|
33
|
+
'</div>';
|
|
34
|
+
|
|
35
|
+
var css = document.createElement('style');
|
|
36
|
+
css.textContent =
|
|
37
|
+
'#__epoint_autofill_btn__ { position:fixed; z-index:999; display:flex; align-items:center; gap:6px;' +
|
|
38
|
+
'padding:7px; background:#fff; border-radius:28px;' +
|
|
39
|
+
'box-shadow:0 2px 16px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.06);' +
|
|
40
|
+
'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;' +
|
|
41
|
+
'user-select:none; }' +
|
|
42
|
+
'#__epoint_autofill_btn__ * { box-sizing:border-box; margin:0; }' +
|
|
43
|
+
|
|
44
|
+
'#__epoint_autofill_btn__ .af-btn { display:inline-flex; align-items:center; gap:0; height:36px; padding:0 10px;' +
|
|
45
|
+
'border:none; border-radius:18px; font-size:13px; font-weight:500; white-space:nowrap;' +
|
|
46
|
+
'cursor:pointer; transition:all 0.2s ease; outline:none; font-family:inherit; }' +
|
|
47
|
+
'#__epoint_autofill_btn__ .af-btn span { display:none; }' +
|
|
48
|
+
'#__epoint_autofill_btn__:hover .af-btn { padding:0 14px; gap:6px; }' +
|
|
49
|
+
'#__epoint_autofill_btn__:hover .af-btn span { display:inline; }' +
|
|
50
|
+
'#__epoint_autofill_btn__ .af-btn:active { transform:scale(0.97); }' +
|
|
51
|
+
|
|
52
|
+
'#__epoint_autofill_btn__ .af-btn-fill { background:#1d4ed8; color:#fff; }' +
|
|
53
|
+
'#__epoint_autofill_btn__:hover .af-btn-fill:hover { background:#1e40af; box-shadow:0 2px 8px rgba(29,78,216,0.3); }' +
|
|
54
|
+
|
|
55
|
+
'#__epoint_autofill_btn__ .af-btn-req { background:#f1f5f9; color:#475569; }' +
|
|
56
|
+
'#__epoint_autofill_btn__:hover .af-btn-req:hover { background:#e2e8f0; color:#1e293b; }' +
|
|
57
|
+
|
|
58
|
+
'#__epoint_autofill_btn__ .af-handle { display:none; align-items:center; justify-content:center;' +
|
|
59
|
+
'width:28px; height:36px; cursor:grab; border-radius:24px; color:#94a3b8;' +
|
|
60
|
+
'transition:background 0.15s; flex-shrink:0; }' +
|
|
61
|
+
'#__epoint_autofill_btn__:hover .af-handle { display:flex; }' +
|
|
62
|
+
'#__epoint_autofill_btn__ .af-handle:hover { background:#f1f5f9; }' +
|
|
63
|
+
'#__epoint_autofill_btn__ .af-handle:active { cursor:grabbing; background:#e2e8f0; }';
|
|
64
|
+
|
|
65
|
+
document.head.appendChild(css);
|
|
66
|
+
document.body.appendChild(bar);
|
|
67
|
+
|
|
68
|
+
bar.style.right = '24px';
|
|
69
|
+
bar.style.bottom = '100px';
|
|
70
|
+
|
|
71
|
+
// ── 根据 bridge 写入的 DOM 属性控制显隐 ──
|
|
72
|
+
function syncVisibility() {
|
|
73
|
+
var visible = document.body.getAttribute('data-epoint-autofill') === '1';
|
|
74
|
+
bar.style.display = visible ? 'flex' : 'none';
|
|
75
|
+
}
|
|
76
|
+
var _visInterval = setInterval(syncVisibility, 500);
|
|
77
|
+
syncVisibility();
|
|
78
|
+
|
|
79
|
+
// ── 填充触发 ──
|
|
80
|
+
bar.addEventListener('click', function (e) {
|
|
81
|
+
var btn = e.target.closest('.af-btn');
|
|
82
|
+
if (!btn) return;
|
|
83
|
+
|
|
84
|
+
document.dispatchEvent(
|
|
85
|
+
new CustomEvent('__epoint_autofill_trigger__', { detail: { fillType: btn.getAttribute('data-fill') } })
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ── 拖拽 ──
|
|
90
|
+
var handle = bar.querySelector('.af-handle');
|
|
91
|
+
var dragging = false, sx, sy, sr, sb;
|
|
92
|
+
|
|
93
|
+
handle.addEventListener('mousedown', function (e) {
|
|
94
|
+
if (e.button !== 0) return;
|
|
95
|
+
dragging = true;
|
|
96
|
+
sx = e.clientX; sy = e.clientY;
|
|
97
|
+
sr = parseInt(bar.style.right, 10);
|
|
98
|
+
sb = parseInt(bar.style.bottom, 10);
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
document.addEventListener('mousemove', function (e) {
|
|
103
|
+
if (!dragging) return;
|
|
104
|
+
bar.style.right = (sr + sx - e.clientX) + 'px';
|
|
105
|
+
bar.style.bottom = (sb + sy - e.clientY) + 'px';
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
document.addEventListener('mouseup', function () { dragging = false; });
|
|
109
|
+
|
|
110
|
+
// ── 移除(Playwright 版:通过 CustomEvent 触发,替代 chrome.runtime.onMessage) ──
|
|
111
|
+
document.addEventListener('epoint:remove-autofill-btn', function () {
|
|
112
|
+
clearInterval(_visInterval);
|
|
113
|
+
var el = document.getElementById('__epoint_autofill_btn__');
|
|
114
|
+
if (el) el.remove();
|
|
115
|
+
var styles = document.querySelectorAll('style');
|
|
116
|
+
for (var i = styles.length - 1; i >= 0; i--) {
|
|
117
|
+
if (styles[i].textContent.indexOf('__epoint_autofill_btn__') > -1) styles[i].remove();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
})();
|