mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-16 06:53:27 +08:00
Merge pull request #1440 from affaan-m/fix/dashboard-terminal-safety
fix(dashboard): harden terminal launch and maximize behavior
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Everything Claude Code (ECC) — Agent Instructions
|
# Everything Claude Code (ECC) — Agent Instructions
|
||||||
|
|
||||||
This is a **production-ready AI coding plugin** providing 47 specialized agents, 181 skills, 79 commands, and automated hook workflows for software development.
|
This is a **production-ready AI coding plugin** providing 48 specialized agents, 183 skills, 79 commands, and automated hook workflows for software development.
|
||||||
|
|
||||||
**Version:** 1.10.0
|
**Version:** 1.10.0
|
||||||
|
|
||||||
@@ -145,8 +145,8 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
|
|||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
agents/ — 47 specialized subagents
|
agents/ — 48 specialized subagents
|
||||||
skills/ — 181 workflow skills and domain knowledge
|
skills/ — 183 workflow skills and domain knowledge
|
||||||
commands/ — 79 slash commands
|
commands/ — 79 slash commands
|
||||||
hooks/ — Trigger-based automations
|
hooks/ — Trigger-based automations
|
||||||
rules/ — Always-follow guidelines (common + per-language)
|
rules/ — Always-follow guidelines (common + per-language)
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -239,7 +239,7 @@ For manual install instructions see the README in the `rules/` folder. When copy
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**That's it!** You now have access to 47 agents, 181 skills, and 79 legacy command shims.
|
**That's it!** You now have access to 48 agents, 183 skills, and 79 legacy command shims.
|
||||||
|
|
||||||
### Dashboard GUI
|
### Dashboard GUI
|
||||||
|
|
||||||
@@ -1205,9 +1205,9 @@ The configuration is automatically detected from `.opencode/opencode.json`.
|
|||||||
|
|
||||||
| Feature | Claude Code | OpenCode | Status |
|
| Feature | Claude Code | OpenCode | Status |
|
||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| Agents | PASS: 47 agents | PASS: 12 agents | **Claude Code leads** |
|
| Agents | PASS: 48 agents | PASS: 12 agents | **Claude Code leads** |
|
||||||
| Commands | PASS: 79 commands | PASS: 31 commands | **Claude Code leads** |
|
| Commands | PASS: 79 commands | PASS: 31 commands | **Claude Code leads** |
|
||||||
| Skills | PASS: 181 skills | PASS: 37 skills | **Claude Code leads** |
|
| Skills | PASS: 183 skills | PASS: 37 skills | **Claude Code leads** |
|
||||||
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
|
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
|
||||||
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
|
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
|
||||||
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
|
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
|
||||||
@@ -1314,9 +1314,9 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
|
|||||||
|
|
||||||
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||||
|---------|------------|------------|-----------|----------|
|
|---------|------------|------------|-----------|----------|
|
||||||
| **Agents** | 47 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |
|
| **Agents** | 48 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |
|
||||||
| **Commands** | 79 | Shared | Instruction-based | 31 |
|
| **Commands** | 79 | Shared | Instruction-based | 31 |
|
||||||
| **Skills** | 181 | Shared | 10 (native format) | 37 |
|
| **Skills** | 183 | Shared | 10 (native format) | 37 |
|
||||||
| **Hook Events** | 8 types | 15 types | None yet | 11 types |
|
| **Hook Events** | 8 types | 15 types | None yet | 11 types |
|
||||||
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks |
|
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks |
|
||||||
| **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions |
|
| **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions |
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ npx ecc-install typescript
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**完成!** 你现在可以使用 47 个代理、181 个技能和 79 个命令。
|
**完成!** 你现在可以使用 48 个代理、183 个技能和 79 个命令。
|
||||||
|
|
||||||
### multi-* 命令需要额外配置
|
### multi-* 命令需要额外配置
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: a11y-architect
|
name: a11y-architect
|
||||||
description: Accessibility Architect specializing in WCAG 2.2 compliance for Web and Native platforms. Use PROACTIVELY when designing UI components, establishing design systems, or auditing code for inclusive user experiences.
|
description: Accessibility Architect specializing in WCAG 2.2 compliance for Web and Native platforms. Use PROACTIVELY when designing UI components, establishing design systems, or auditing code for inclusive user experiences.
|
||||||
|
model: sonnet
|
||||||
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Everything Claude Code (ECC) — 智能体指令
|
# Everything Claude Code (ECC) — 智能体指令
|
||||||
|
|
||||||
这是一个**生产就绪的 AI 编码插件**,提供 47 个专业代理、181 项技能、79 条命令以及自动化钩子工作流,用于软件开发。
|
这是一个**生产就绪的 AI 编码插件**,提供 48 个专业代理、183 项技能、79 条命令以及自动化钩子工作流,用于软件开发。
|
||||||
|
|
||||||
**版本:** 1.10.0
|
**版本:** 1.10.0
|
||||||
|
|
||||||
@@ -146,8 +146,8 @@
|
|||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
```
|
```
|
||||||
agents/ — 47 个专业子代理
|
agents/ — 48 个专业子代理
|
||||||
skills/ — 181 个工作流技能和领域知识
|
skills/ — 183 个工作流技能和领域知识
|
||||||
commands/ — 79 个斜杠命令
|
commands/ — 79 个斜杠命令
|
||||||
hooks/ — 基于触发的自动化
|
hooks/ — 基于触发的自动化
|
||||||
rules/ — 始终遵循的指导方针(通用 + 每种语言)
|
rules/ — 始终遵循的指导方针(通用 + 每种语言)
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ npx ecc-install typescript
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**搞定!** 你现在可以使用 47 个智能体、181 项技能和 79 个命令了。
|
**搞定!** 你现在可以使用 48 个智能体、183 项技能和 79 个命令了。
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
@@ -1094,9 +1094,9 @@ opencode
|
|||||||
|
|
||||||
| 功能特性 | Claude Code | OpenCode | 状态 |
|
| 功能特性 | Claude Code | OpenCode | 状态 |
|
||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| 智能体 | PASS: 47 个 | PASS: 12 个 | **Claude Code 领先** |
|
| 智能体 | PASS: 48 个 | PASS: 12 个 | **Claude Code 领先** |
|
||||||
| 命令 | PASS: 79 个 | PASS: 31 个 | **Claude Code 领先** |
|
| 命令 | PASS: 79 个 | PASS: 31 个 | **Claude Code 领先** |
|
||||||
| 技能 | PASS: 181 项 | PASS: 37 项 | **Claude Code 领先** |
|
| 技能 | PASS: 183 项 | PASS: 37 项 | **Claude Code 领先** |
|
||||||
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
|
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
|
||||||
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
|
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
|
||||||
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
|
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
|
||||||
@@ -1206,9 +1206,9 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
|
|||||||
|
|
||||||
| 功能特性 | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
| 功能特性 | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||||
|---------|------------|------------|-----------|----------|
|
|---------|------------|------------|-----------|----------|
|
||||||
| **智能体** | 47 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
| **智能体** | 48 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
||||||
| **命令** | 79 | 共享 | 基于指令 | 31 |
|
| **命令** | 79 | 共享 | 基于指令 | 31 |
|
||||||
| **技能** | 181 | 共享 | 10 (原生格式) | 37 |
|
| **技能** | 183 | 共享 | 10 (原生格式) | 37 |
|
||||||
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
|
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
|
||||||
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 |
|
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 |
|
||||||
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
|
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ import tkinter as tk
|
|||||||
from tkinter import ttk, scrolledtext, messagebox
|
from tkinter import ttk, scrolledtext, messagebox
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import subprocess
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
|
from scripts.lib.ecc_dashboard_runtime import build_terminal_launch, maximize_window
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# DATA LOADERS - Load ECC data from the project
|
# DATA LOADERS - Load ECC data from the project
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -18,6 +21,7 @@ def get_project_path() -> str:
|
|||||||
"""Get the ECC project path - assumes this script is run from the project dir"""
|
"""Get the ECC project path - assumes this script is run from the project dir"""
|
||||||
return os.path.dirname(os.path.abspath(__file__))
|
return os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
def load_agents(project_path: str) -> List[Dict]:
|
def load_agents(project_path: str) -> List[Dict]:
|
||||||
"""Load agents from AGENTS.md"""
|
"""Load agents from AGENTS.md"""
|
||||||
agents_file = os.path.join(project_path, "AGENTS.md")
|
agents_file = os.path.join(project_path, "AGENTS.md")
|
||||||
@@ -257,7 +261,7 @@ class ECCDashboard(tk.Tk):
|
|||||||
self.project_path = get_project_path()
|
self.project_path = get_project_path()
|
||||||
self.title("ECC Dashboard - Everything Claude Code")
|
self.title("ECC Dashboard - Everything Claude Code")
|
||||||
|
|
||||||
self.state('zoomed')
|
maximize_window(self)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.icon_image = tk.PhotoImage(file='assets/images/ecc-logo.png')
|
self.icon_image = tk.PhotoImage(file='assets/images/ecc-logo.png')
|
||||||
@@ -789,14 +793,9 @@ Project: github.com/affaan-m/everything-claude-code"""
|
|||||||
|
|
||||||
def open_terminal(self):
|
def open_terminal(self):
|
||||||
"""Open terminal at project path"""
|
"""Open terminal at project path"""
|
||||||
import subprocess
|
|
||||||
path = self.path_entry.get()
|
path = self.path_entry.get()
|
||||||
if os.name == 'nt': # Windows
|
argv, kwargs = build_terminal_launch(path)
|
||||||
subprocess.Popen(['cmd', '/c', 'start', 'cmd', '/k', f'cd /d "{path}"'])
|
subprocess.Popen(argv, **kwargs)
|
||||||
elif os.uname().sysname == 'Darwin': # macOS
|
|
||||||
subprocess.Popen(['open', '-a', 'Terminal', path])
|
|
||||||
else: # Linux
|
|
||||||
subprocess.Popen(['x-terminal-emulator', '-e', f'cd {path}'])
|
|
||||||
|
|
||||||
def open_readme(self):
|
def open_readme(self):
|
||||||
"""Open README in default browser/reader"""
|
"""Open README in default browser/reader"""
|
||||||
@@ -911,4 +910,4 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -121,7 +121,17 @@ function isChecked(key) {
|
|||||||
|
|
||||||
function sanitizePath(filePath) {
|
function sanitizePath(filePath) {
|
||||||
// Strip control chars (including null), bidi overrides, and newlines
|
// Strip control chars (including null), bidi overrides, and newlines
|
||||||
return filePath.replace(/[\x00-\x1f\x7f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, ' ').trim().slice(0, 500);
|
let sanitized = '';
|
||||||
|
for (const char of String(filePath || '')) {
|
||||||
|
const code = char.codePointAt(0);
|
||||||
|
const isAsciiControl = code <= 0x1f || code === 0x7f;
|
||||||
|
const isBidiOverride =
|
||||||
|
(code >= 0x200e && code <= 0x200f) ||
|
||||||
|
(code >= 0x202a && code <= 0x202e) ||
|
||||||
|
(code >= 0x2066 && code <= 0x2069);
|
||||||
|
sanitized += isAsciiControl || isBidiOverride ? ' ' : char;
|
||||||
|
}
|
||||||
|
return sanitized.trim().slice(0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Gate messages ---
|
// --- Gate messages ---
|
||||||
|
|||||||
61
scripts/lib/ecc_dashboard_runtime.py
Normal file
61
scripts/lib/ecc_dashboard_runtime.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Runtime helpers for ecc_dashboard.py that do not depend on tkinter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
from typing import Optional, Tuple, Dict, List
|
||||||
|
|
||||||
|
|
||||||
|
def maximize_window(window) -> None:
|
||||||
|
"""Maximize the dashboard window using the safest supported method."""
|
||||||
|
try:
|
||||||
|
window.state('zoomed')
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
system_name = platform.system()
|
||||||
|
if system_name == 'Linux':
|
||||||
|
try:
|
||||||
|
window.attributes('-zoomed', True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
elif system_name == 'Darwin':
|
||||||
|
try:
|
||||||
|
window.attributes('-fullscreen', True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def build_terminal_launch(
|
||||||
|
path: str,
|
||||||
|
*,
|
||||||
|
os_name: Optional[str] = None,
|
||||||
|
system_name: Optional[str] = None,
|
||||||
|
) -> Tuple[List[str], Dict[str, object]]:
|
||||||
|
"""Return safe argv/kwargs for opening a terminal rooted at the requested path."""
|
||||||
|
resolved_os_name = os_name or os.name
|
||||||
|
resolved_system_name = system_name or platform.system()
|
||||||
|
|
||||||
|
if resolved_os_name == 'nt':
|
||||||
|
creationflags = getattr(subprocess, 'CREATE_NEW_CONSOLE', 0)
|
||||||
|
return (
|
||||||
|
['cmd.exe', '/k', 'cd', '/d', path],
|
||||||
|
{
|
||||||
|
'cwd': path,
|
||||||
|
'creationflags': creationflags,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if resolved_system_name == 'Darwin':
|
||||||
|
return (['open', '-a', 'Terminal', path], {})
|
||||||
|
|
||||||
|
return (
|
||||||
|
['x-terminal-emulator', '-e', 'bash', '-lc', 'cd -- "$1"; exec bash', 'bash', path],
|
||||||
|
{},
|
||||||
|
)
|
||||||
128
tests/scripts/ecc-dashboard.test.js
Normal file
128
tests/scripts/ecc-dashboard.test.js
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* Behavioral tests for ecc_dashboard.py helper functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const path = require('path');
|
||||||
|
const { spawnSync } = require('child_process');
|
||||||
|
|
||||||
|
const repoRoot = path.join(__dirname, '..', '..');
|
||||||
|
const runtimeHelpersPath = path.join(repoRoot, 'scripts', 'lib', 'ecc_dashboard_runtime.py');
|
||||||
|
|
||||||
|
function test(name, fn) {
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
console.log(` ✓ ${name}`);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(` ✗ ${name}`);
|
||||||
|
console.log(` Error: ${error.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runPython(source) {
|
||||||
|
const candidates = process.platform === 'win32' ? ['python', 'python3'] : ['python3', 'python'];
|
||||||
|
let lastError = null;
|
||||||
|
|
||||||
|
for (const command of candidates) {
|
||||||
|
const result = spawnSync(command, ['-c', source], {
|
||||||
|
cwd: repoRoot,
|
||||||
|
encoding: 'utf8',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error && result.error.code === 'ENOENT') {
|
||||||
|
lastError = result.error;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status !== 0) {
|
||||||
|
throw new Error((result.stderr || result.stdout || '').trim() || `${command} exited ${result.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.stdout.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lastError || new Error('No Python interpreter available');
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests() {
|
||||||
|
console.log('\n=== Testing ecc_dashboard.py ===\n');
|
||||||
|
|
||||||
|
let passed = 0;
|
||||||
|
let failed = 0;
|
||||||
|
|
||||||
|
if (test('build_terminal_launch keeps Linux path separate from shell command text', () => {
|
||||||
|
const output = runPython(`
|
||||||
|
import importlib.util, json
|
||||||
|
spec = importlib.util.spec_from_file_location("ecc_dashboard_runtime", r"""${runtimeHelpersPath}""")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
argv, kwargs = module.build_terminal_launch('/tmp/proj; rm -rf ~', os_name='posix', system_name='Linux')
|
||||||
|
print(json.dumps({'argv': argv, 'kwargs': kwargs}))
|
||||||
|
`);
|
||||||
|
const parsed = JSON.parse(output);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
parsed.argv,
|
||||||
|
['x-terminal-emulator', '-e', 'bash', '-lc', 'cd -- "$1"; exec bash', 'bash', '/tmp/proj; rm -rf ~']
|
||||||
|
);
|
||||||
|
assert.deepStrictEqual(parsed.kwargs, {});
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('build_terminal_launch uses cwd + CREATE_NEW_CONSOLE style launch on Windows', () => {
|
||||||
|
const output = runPython(`
|
||||||
|
import importlib.util, json
|
||||||
|
spec = importlib.util.spec_from_file_location("ecc_dashboard_runtime", r"""${runtimeHelpersPath}""")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
argv, kwargs = module.build_terminal_launch(r'C:\\\\Users\\\\user\\\\proj & del C:\\\\*', os_name='nt', system_name='Windows')
|
||||||
|
print(json.dumps({'argv': argv, 'kwargs': kwargs}))
|
||||||
|
`);
|
||||||
|
const parsed = JSON.parse(output);
|
||||||
|
assert.deepStrictEqual(parsed.argv.slice(0, 4), ['cmd.exe', '/k', 'cd', '/d']);
|
||||||
|
assert.strictEqual(parsed.argv[4], parsed.kwargs.cwd);
|
||||||
|
assert.ok(parsed.argv[4].includes('proj & del'), 'path should remain a literal argv entry');
|
||||||
|
assert.ok(parsed.argv[4].includes('C:'), 'windows drive prefix should be preserved');
|
||||||
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed.kwargs, 'creationflags'));
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('maximize_window falls back to Linux zoom attribute when zoomed state is unsupported', () => {
|
||||||
|
const output = runPython(`
|
||||||
|
import importlib.util, json
|
||||||
|
spec = importlib.util.spec_from_file_location("ecc_dashboard_runtime", r"""${runtimeHelpersPath}""")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
|
||||||
|
class FakeWindow:
|
||||||
|
def __init__(self):
|
||||||
|
self.calls = []
|
||||||
|
|
||||||
|
def state(self, value):
|
||||||
|
self.calls.append(['state', value])
|
||||||
|
raise RuntimeError('bad argument "zoomed"')
|
||||||
|
|
||||||
|
def attributes(self, name, value):
|
||||||
|
self.calls.append(['attributes', name, value])
|
||||||
|
|
||||||
|
original = module.platform.system
|
||||||
|
module.platform.system = lambda: 'Linux'
|
||||||
|
try:
|
||||||
|
window = FakeWindow()
|
||||||
|
module.maximize_window(window)
|
||||||
|
finally:
|
||||||
|
module.platform.system = original
|
||||||
|
|
||||||
|
print(json.dumps(window.calls))
|
||||||
|
`);
|
||||||
|
const parsed = JSON.parse(output);
|
||||||
|
assert.deepStrictEqual(parsed, [
|
||||||
|
['state', 'zoomed'],
|
||||||
|
['attributes', '-zoomed', true],
|
||||||
|
]);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||||
|
process.exit(failed > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests();
|
||||||
Reference in New Issue
Block a user