💬 Commit message: Update 2026-02-06 21:50:58, 15 files, 983 lines
📁 Files changed: 15 📝 Lines changed: 983 • .gitignore • browser.ts.html • clover.xml • coverage-final.json • index.html • logger.ts.html • server.ts.html • types.ts.html • package.json • browser.integration.test.ts • logger.test.ts • server.test.ts • vitest.all.config.ts • vitest.config.ts • vitest.integration.config.ts
This commit is contained in:
@@ -2,3 +2,5 @@
|
||||
/public/
|
||||
/dist/
|
||||
/screenshots/
|
||||
coverage/
|
||||
screenshots/
|
||||
|
||||
+156
-156
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">58.42% </span>
|
||||
<span class="strong">91.01% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>52/89</span>
|
||||
<span class='fraction'>81/89</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">75.67% </span>
|
||||
<span class="strong">89.18% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>28/37</span>
|
||||
<span class='fraction'>33/37</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">80.95% </span>
|
||||
<span class="strong">85.71% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>17/21</span>
|
||||
<span class='fraction'>18/21</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">58.42% </span>
|
||||
<span class="strong">91.01% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>52/89</span>
|
||||
<span class='fraction'>81/89</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line medium'></div>
|
||||
<div class='status-line high'></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
@@ -270,13 +270,13 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -284,43 +284,43 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">44x</span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">32x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -329,8 +329,8 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
@@ -346,111 +346,67 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">28x</span>
|
||||
<span class="cline-any cline-yes">28x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -458,6 +414,50 @@
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -485,44 +485,44 @@ export class ClaudeBrowser {
|
||||
};
|
||||
}
|
||||
|
||||
async <span class="fstat-no" title="function not covered" >launch(): Promise<void> {</span>
|
||||
<span class="cstat-no" title="statement not covered" > this.browser = await webkit.launch({ headless: this.options.headless });</span>
|
||||
<span class="cstat-no" title="statement not covered" > this.context = await this.browser.newContext({</span>
|
||||
async launch(): Promise<void> {
|
||||
this.browser = await webkit.launch({ headless: this.options.headless });
|
||||
this.context = await this.browser.newContext({
|
||||
viewport: {
|
||||
width: this.options.width,
|
||||
height: this.options.height,
|
||||
},
|
||||
});
|
||||
<span class="cstat-no" title="statement not covered" > this.page = await this.context.newPage();</span>
|
||||
this.page = await this.context.newPage();
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (this.browser) {
|
||||
<span class="cstat-no" title="statement not covered" > await this.browser.close();</span>
|
||||
<span class="cstat-no" title="statement not covered" > this.browser = null;</span>
|
||||
<span class="cstat-no" title="statement not covered" > this.context = null;</span>
|
||||
<span class="cstat-no" title="statement not covered" > this.page = null;</span>
|
||||
if (this.browser) {
|
||||
await this.browser.close();
|
||||
this.browser = null;
|
||||
this.context = null;
|
||||
this.page = null;
|
||||
}
|
||||
}
|
||||
|
||||
private ensurePage(): Page {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!this.page) {
|
||||
if (!this.page) {
|
||||
throw new Error('Browser not launched. Call launch() first.');
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > return this.page;</span>
|
||||
return this.page;
|
||||
}
|
||||
|
||||
async goto(url: string): Promise<{ url: string; title: string }> {
|
||||
const page = this.ensurePage();
|
||||
await page.goto(url, { waitUntil: 'networkidle' });
|
||||
<span class="cstat-no" title="statement not covered" > return { url: page.url(), title: await page.title() };</span>
|
||||
return { url: page.url(), title: await page.title() };
|
||||
}
|
||||
|
||||
async click(selector: string): Promise<{ url: string }> {
|
||||
const page = this.ensurePage();
|
||||
await page.click(selector);
|
||||
<span class="cstat-no" title="statement not covered" > await page.waitForLoadState('networkidle').catch(<span class="fstat-no" title="function not covered" >() => {</span>});</span>
|
||||
<span class="cstat-no" title="statement not covered" > return { url: page.url() };</span>
|
||||
await page.waitForLoadState('networkidle').catch(<span class="fstat-no" title="function not covered" >() => {</span>});
|
||||
return { url: page.url() };
|
||||
}
|
||||
|
||||
async type(selector: string, text: string): Promise<void> {
|
||||
@@ -551,7 +551,7 @@ export class ClaudeBrowser {
|
||||
const page = this.ensurePage();
|
||||
const resolvedPath = resolve(path || <span class="branch-1 cbranch-no" title="branch not covered" >'screenshot.png')</span>;
|
||||
const buffer = await page.screenshot({ path: resolvedPath, fullPage });
|
||||
<span class="cstat-no" title="statement not covered" > return { path: resolvedPath, buffer };</span>
|
||||
return { path: resolvedPath, buffer };
|
||||
}
|
||||
|
||||
async getUrl(): Promise<{ url: string; title: string }> {
|
||||
@@ -562,25 +562,25 @@ export class ClaudeBrowser {
|
||||
async getHtml(full = false): Promise<string> {
|
||||
const page = this.ensurePage();
|
||||
const html = await page.content();
|
||||
<span class="cstat-no" title="statement not covered" > return full ? html : html.slice(0, 10000);</span>
|
||||
return full ? html : html.slice(0, 10000);
|
||||
}
|
||||
|
||||
async back(): Promise<{ url: string }> {
|
||||
const page = this.ensurePage();
|
||||
await page.goBack();
|
||||
<span class="cstat-no" title="statement not covered" > return { url: page.url() };</span>
|
||||
return { url: page.url() };
|
||||
}
|
||||
|
||||
async forward(): Promise<{ url: string }> {
|
||||
const page = this.ensurePage();
|
||||
await page.goForward();
|
||||
<span class="cstat-no" title="statement not covered" > return { url: page.url() };</span>
|
||||
return { url: page.url() };
|
||||
}
|
||||
|
||||
async reload(): Promise<{ url: string }> {
|
||||
const page = this.ensurePage();
|
||||
await page.reload();
|
||||
<span class="cstat-no" title="statement not covered" > return { url: page.url() };</span>
|
||||
return { url: page.url() };
|
||||
}
|
||||
|
||||
async wait(ms = 1000): Promise<void> {
|
||||
@@ -589,10 +589,10 @@ export class ClaudeBrowser {
|
||||
}
|
||||
|
||||
async newPage(): Promise<void> {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!this.context) {
|
||||
if (!this.context) {
|
||||
throw new Error('Browser not launched. Call launch() first.');
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > this.page = await this.context.newPage();</span>
|
||||
this.page = await this.context.newPage();
|
||||
}
|
||||
|
||||
async eval(script: string): Promise<unknown> {
|
||||
@@ -605,11 +605,11 @@ export class ClaudeBrowser {
|
||||
switch (cmd.cmd) {
|
||||
case 'goto': {
|
||||
const result = await this.goto(cmd.url);
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, ...result };</span>
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
case 'click': {
|
||||
const result = await this.click(cmd.selector);
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, ...result };</span>
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
case 'type': {
|
||||
await this.type(cmd.selector, cmd.text);
|
||||
@@ -617,39 +617,39 @@ export class ClaudeBrowser {
|
||||
}
|
||||
case 'query': {
|
||||
const elements = await this.query(cmd.selector);
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, count: elements.length, elements };</span>
|
||||
return { ok: true, count: elements.length, elements };
|
||||
}
|
||||
case 'screenshot': {
|
||||
const result = await this.screenshot(cmd.path, cmd.fullPage);
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, path: result.path };</span>
|
||||
return { ok: true, path: result.path };
|
||||
}
|
||||
case 'url': {
|
||||
const result = await this.getUrl();
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, ...result };</span>
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
case 'html': {
|
||||
const html = await this.getHtml(cmd.full);
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, html };</span>
|
||||
return { ok: true, html };
|
||||
}
|
||||
case 'back': {
|
||||
const result = await this.back();
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, ...result };</span>
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
case 'forward': {
|
||||
const result = await this.forward();
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, ...result };</span>
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
case 'reload': {
|
||||
const result = await this.reload();
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, ...result };</span>
|
||||
return { ok: true, ...result };
|
||||
}
|
||||
case 'wait': {
|
||||
await this.wait(cmd.ms);
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true };</span>
|
||||
return { ok: true };
|
||||
}
|
||||
case 'newpage': {
|
||||
await this.newPage();
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true };</span>
|
||||
return { ok: true };
|
||||
}
|
||||
case 'close': {
|
||||
await this.close();
|
||||
@@ -657,7 +657,7 @@ export class ClaudeBrowser {
|
||||
}
|
||||
case 'eval': {
|
||||
const result = await this.eval(cmd.script);
|
||||
<span class="cstat-no" title="statement not covered" > return { ok: true, result };</span>
|
||||
return { ok: true, result };
|
||||
}
|
||||
<span class="branch-14 cbranch-no" title="branch not covered" > default: {</span>
|
||||
const _exhaustive: never = <span class="cstat-no" title="statement not covered" >cmd;</span>
|
||||
@@ -676,7 +676,7 @@ export class ClaudeBrowser {
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2026-02-06T20:43:25.074Z
|
||||
at 2026-02-06T20:50:54.063Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<coverage generated="1770410605079" clover="3.2.0">
|
||||
<project timestamp="1770410605079" name="All files">
|
||||
<metrics statements="177" coveredstatements="108" conditionals="89" coveredconditionals="63" methods="50" coveredmethods="38" elements="316" coveredelements="209" complexity="0" loc="177" ncloc="177" packages="1" files="4" classes="4"/>
|
||||
<file name="browser.ts" path="/Users/chi/Projects/claude-browse/src/browser.ts">
|
||||
<metrics statements="89" coveredstatements="52" conditionals="37" coveredconditionals="28" methods="21" coveredmethods="17"/>
|
||||
<line num="6" count="6" type="stmt"/>
|
||||
<line num="7" count="6" type="stmt"/>
|
||||
<line num="8" count="6" type="stmt"/>
|
||||
<line num="12" count="6" type="stmt"/>
|
||||
<line num="20" count="0" type="stmt"/>
|
||||
<line num="21" count="0" type="stmt"/>
|
||||
<line num="27" count="0" type="stmt"/>
|
||||
<line num="31" count="1" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="32" count="0" type="stmt"/>
|
||||
<line num="33" count="0" type="stmt"/>
|
||||
<line num="34" count="0" type="stmt"/>
|
||||
<line num="35" count="0" type="stmt"/>
|
||||
<line num="40" count="12" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="41" count="12" type="stmt"/>
|
||||
<line num="43" count="0" type="stmt"/>
|
||||
<line num="47" count="1" type="stmt"/>
|
||||
<line num="48" count="1" type="stmt"/>
|
||||
<line num="49" count="0" type="stmt"/>
|
||||
<line num="53" count="1" type="stmt"/>
|
||||
<line num="54" count="1" type="stmt"/>
|
||||
<line num="55" count="0" type="stmt"/>
|
||||
<line num="56" count="0" type="stmt"/>
|
||||
<line num="60" count="1" type="stmt"/>
|
||||
<line num="61" count="1" type="stmt"/>
|
||||
<line num="65" count="1" type="stmt"/>
|
||||
<line num="66" count="1" type="stmt"/>
|
||||
<line num="67" count="0" type="stmt"/>
|
||||
<line num="68" count="0" type="stmt"/>
|
||||
<line num="69" count="0" type="stmt"/>
|
||||
<line num="70" count="0" type="stmt"/>
|
||||
<line num="72" count="0" type="stmt"/>
|
||||
<line num="82" count="1" type="stmt"/>
|
||||
<line num="83" count="1" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="84" count="1" type="stmt"/>
|
||||
<line num="85" count="0" type="stmt"/>
|
||||
<line num="89" count="1" type="stmt"/>
|
||||
<line num="90" count="1" type="stmt"/>
|
||||
<line num="94" count="1" type="stmt"/>
|
||||
<line num="95" count="1" type="stmt"/>
|
||||
<line num="96" count="0" type="cond" truecount="0" falsecount="2"/>
|
||||
<line num="100" count="1" type="stmt"/>
|
||||
<line num="101" count="1" type="stmt"/>
|
||||
<line num="102" count="0" type="stmt"/>
|
||||
<line num="106" count="1" type="stmt"/>
|
||||
<line num="107" count="1" type="stmt"/>
|
||||
<line num="108" count="0" type="stmt"/>
|
||||
<line num="112" count="1" type="stmt"/>
|
||||
<line num="113" count="1" type="stmt"/>
|
||||
<line num="114" count="0" type="stmt"/>
|
||||
<line num="118" count="1" type="stmt"/>
|
||||
<line num="119" count="1" type="stmt"/>
|
||||
<line num="123" count="1" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="124" count="1" type="stmt"/>
|
||||
<line num="126" count="0" type="stmt"/>
|
||||
<line num="130" count="1" type="stmt"/>
|
||||
<line num="131" count="1" type="stmt"/>
|
||||
<line num="135" count="14" type="stmt"/>
|
||||
<line num="136" count="14" type="cond" truecount="14" falsecount="1"/>
|
||||
<line num="138" count="1" type="stmt"/>
|
||||
<line num="139" count="0" type="stmt"/>
|
||||
<line num="142" count="1" type="stmt"/>
|
||||
<line num="143" count="0" type="stmt"/>
|
||||
<line num="146" count="1" type="stmt"/>
|
||||
<line num="147" count="0" type="stmt"/>
|
||||
<line num="150" count="1" type="stmt"/>
|
||||
<line num="151" count="0" type="stmt"/>
|
||||
<line num="154" count="1" type="stmt"/>
|
||||
<line num="155" count="0" type="stmt"/>
|
||||
<line num="158" count="1" type="stmt"/>
|
||||
<line num="159" count="0" type="stmt"/>
|
||||
<line num="162" count="1" type="stmt"/>
|
||||
<line num="163" count="0" type="stmt"/>
|
||||
<line num="166" count="1" type="stmt"/>
|
||||
<line num="167" count="0" type="stmt"/>
|
||||
<line num="170" count="1" type="stmt"/>
|
||||
<line num="171" count="0" type="stmt"/>
|
||||
<line num="174" count="1" type="stmt"/>
|
||||
<line num="175" count="0" type="stmt"/>
|
||||
<line num="178" count="1" type="stmt"/>
|
||||
<line num="179" count="0" type="stmt"/>
|
||||
<line num="182" count="1" type="stmt"/>
|
||||
<line num="183" count="0" type="stmt"/>
|
||||
<line num="186" count="1" type="stmt"/>
|
||||
<line num="187" count="1" type="stmt"/>
|
||||
<line num="190" count="1" type="stmt"/>
|
||||
<line num="191" count="0" type="stmt"/>
|
||||
<line num="194" count="0" type="stmt"/>
|
||||
<line num="195" count="0" type="stmt"/>
|
||||
<line num="199" count="13" type="stmt"/>
|
||||
</file>
|
||||
<file name="logger.ts" path="/Users/chi/Projects/claude-browse/src/logger.ts">
|
||||
<metrics statements="37" coveredstatements="34" conditionals="42" coveredconditionals="28" methods="16" coveredmethods="13"/>
|
||||
<line num="5" count="2" type="stmt"/>
|
||||
<line num="23" count="2" type="stmt"/>
|
||||
<line num="41" count="23" type="stmt"/>
|
||||
<line num="45" count="3" type="cond" truecount="2" falsecount="0"/>
|
||||
<line num="60" count="18" type="cond" truecount="8" falsecount="1"/>
|
||||
<line num="62" count="4" type="stmt"/>
|
||||
<line num="65" count="5" type="stmt"/>
|
||||
<line num="67" count="2" type="stmt"/>
|
||||
<line num="69" count="2" type="cond" truecount="2" falsecount="0"/>
|
||||
<line num="71" count="1" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="73" count="1" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="75" count="0" type="cond" truecount="0" falsecount="2"/>
|
||||
<line num="77" count="3" type="stmt"/>
|
||||
<line num="82" count="11" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="83" count="11" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="84" count="11" type="stmt"/>
|
||||
<line num="85" count="11" type="cond" truecount="2" falsecount="0"/>
|
||||
<line num="86" count="11" type="stmt"/>
|
||||
<line num="100" count="2" type="stmt"/>
|
||||
<line num="101" count="3" type="stmt"/>
|
||||
<line num="102" count="2" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="103" count="2" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="104" count="0" type="cond" truecount="0" falsecount="2"/>
|
||||
<line num="105" count="2" type="stmt"/>
|
||||
<line num="106" count="1" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="107" count="0" type="cond" truecount="0" falsecount="2"/>
|
||||
<line num="111" count="12" type="cond" truecount="2" falsecount="0"/>
|
||||
<line num="112" count="1" type="stmt"/>
|
||||
<line num="114" count="11" type="stmt"/>
|
||||
<line num="115" count="11" type="cond" truecount="2" falsecount="0"/>
|
||||
<line num="116" count="12" type="cond" truecount="2" falsecount="0"/>
|
||||
<line num="117" count="12" type="stmt"/>
|
||||
<line num="123" count="6" type="stmt"/>
|
||||
<line num="125" count="9" type="stmt"/>
|
||||
<line num="128" count="9" type="stmt"/>
|
||||
<line num="134" count="2" type="stmt"/>
|
||||
<line num="137" count="2" type="stmt"/>
|
||||
</file>
|
||||
<file name="server.ts" path="/Users/chi/Projects/claude-browse/src/server.ts">
|
||||
<metrics statements="51" coveredstatements="22" conditionals="10" coveredconditionals="7" methods="13" coveredmethods="8"/>
|
||||
<line num="13" count="0" type="stmt"/>
|
||||
<line num="14" count="0" type="stmt"/>
|
||||
<line num="15" count="0" type="stmt"/>
|
||||
<line num="16" count="0" type="stmt"/>
|
||||
<line num="17" count="0" type="stmt"/>
|
||||
<line num="18" count="0" type="stmt"/>
|
||||
<line num="19" count="0" type="stmt"/>
|
||||
<line num="22" count="0" type="stmt"/>
|
||||
<line num="25" count="0" type="stmt"/>
|
||||
<line num="26" count="0" type="stmt"/>
|
||||
<line num="27" count="0" type="stmt"/>
|
||||
<line num="30" count="0" type="stmt"/>
|
||||
<line num="35" count="3" type="stmt"/>
|
||||
<line num="36" count="3" type="stmt"/>
|
||||
<line num="40" count="3" type="stmt"/>
|
||||
<line num="41" count="3" type="cond" truecount="2" falsecount="0"/>
|
||||
<line num="42" count="3" type="stmt"/>
|
||||
<line num="43" count="3" type="stmt"/>
|
||||
<line num="47" count="3" type="stmt"/>
|
||||
<line num="48" count="3" type="stmt"/>
|
||||
<line num="52" count="7" type="stmt"/>
|
||||
<line num="56" count="7" type="stmt"/>
|
||||
<line num="57" count="7" type="cond" truecount="2" falsecount="0"/>
|
||||
<line num="58" count="7" type="stmt"/>
|
||||
<line num="60" count="7" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="61" count="0" type="stmt"/>
|
||||
<line num="62" count="0" type="stmt"/>
|
||||
<line num="63" count="0" type="stmt"/>
|
||||
<line num="64" count="0" type="stmt"/>
|
||||
<line num="67" count="7" type="stmt"/>
|
||||
<line num="68" count="7" type="stmt"/>
|
||||
<line num="69" count="7" type="stmt"/>
|
||||
<line num="71" count="0" type="stmt"/>
|
||||
<line num="72" count="0" type="stmt"/>
|
||||
<line num="73" count="0" type="stmt"/>
|
||||
<line num="78" count="0" type="stmt"/>
|
||||
<line num="80" count="0" type="stmt"/>
|
||||
<line num="81" count="0" type="stmt"/>
|
||||
<line num="82" count="0" type="stmt"/>
|
||||
<line num="83" count="0" type="stmt"/>
|
||||
<line num="89" count="1" type="stmt"/>
|
||||
<line num="90" count="1" type="cond" truecount="1" falsecount="1"/>
|
||||
<line num="91" count="0" type="stmt"/>
|
||||
<line num="92" count="0" type="stmt"/>
|
||||
<line num="94" count="1" type="stmt"/>
|
||||
<line num="95" count="1" type="stmt"/>
|
||||
<line num="99" count="2" type="stmt"/>
|
||||
<line num="103" count="7" type="stmt"/>
|
||||
<line num="108" count="0" type="stmt"/>
|
||||
<line num="109" count="0" type="stmt"/>
|
||||
<line num="110" count="0" type="stmt"/>
|
||||
</file>
|
||||
<file name="types.ts" path="/Users/chi/Projects/claude-browse/src/types.ts">
|
||||
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
|
||||
</file>
|
||||
</project>
|
||||
</coverage>
|
||||
File diff suppressed because one or more lines are too long
+38
-38
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">60.89% </span>
|
||||
<span class="strong">80.44% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>109/179</span>
|
||||
<span class='fraction'>144/179</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">70.78% </span>
|
||||
<span class="strong">85.39% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>63/89</span>
|
||||
<span class='fraction'>76/89</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">76% </span>
|
||||
<span class="strong">82% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>38/50</span>
|
||||
<span class='fraction'>41/50</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">61.01% </span>
|
||||
<span class="strong">80.79% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>108/177</span>
|
||||
<span class='fraction'>143/177</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line medium'></div>
|
||||
<div class='status-line high'></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
@@ -79,48 +79,48 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file medium" data-value="browser.ts"><a href="browser.ts.html">browser.ts</a></td>
|
||||
<td data-value="58.42" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 58%"></div><div class="cover-empty" style="width: 42%"></div></div>
|
||||
<td class="file high" data-value="browser.ts"><a href="browser.ts.html">browser.ts</a></td>
|
||||
<td data-value="91.01" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 91%"></div><div class="cover-empty" style="width: 9%"></div></div>
|
||||
</td>
|
||||
<td data-value="58.42" class="pct medium">58.42%</td>
|
||||
<td data-value="89" class="abs medium">52/89</td>
|
||||
<td data-value="75.67" class="pct medium">75.67%</td>
|
||||
<td data-value="37" class="abs medium">28/37</td>
|
||||
<td data-value="80.95" class="pct high">80.95%</td>
|
||||
<td data-value="21" class="abs high">17/21</td>
|
||||
<td data-value="58.42" class="pct medium">58.42%</td>
|
||||
<td data-value="89" class="abs medium">52/89</td>
|
||||
<td data-value="91.01" class="pct high">91.01%</td>
|
||||
<td data-value="89" class="abs high">81/89</td>
|
||||
<td data-value="89.18" class="pct high">89.18%</td>
|
||||
<td data-value="37" class="abs high">33/37</td>
|
||||
<td data-value="85.71" class="pct high">85.71%</td>
|
||||
<td data-value="21" class="abs high">18/21</td>
|
||||
<td data-value="91.01" class="pct high">91.01%</td>
|
||||
<td data-value="89" class="abs high">81/89</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="logger.ts"><a href="logger.ts.html">logger.ts</a></td>
|
||||
<td data-value="89.47" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 89%"></div><div class="cover-empty" style="width: 11%"></div></div>
|
||||
<td data-value="97.36" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 97%"></div><div class="cover-empty" style="width: 3%"></div></div>
|
||||
</td>
|
||||
<td data-value="89.47" class="pct high">89.47%</td>
|
||||
<td data-value="38" class="abs high">34/38</td>
|
||||
<td data-value="66.66" class="pct medium">66.66%</td>
|
||||
<td data-value="42" class="abs medium">28/42</td>
|
||||
<td data-value="81.25" class="pct high">81.25%</td>
|
||||
<td data-value="16" class="abs high">13/16</td>
|
||||
<td data-value="91.89" class="pct high">91.89%</td>
|
||||
<td data-value="37" class="abs high">34/37</td>
|
||||
<td data-value="97.36" class="pct high">97.36%</td>
|
||||
<td data-value="38" class="abs high">37/38</td>
|
||||
<td data-value="85.71" class="pct high">85.71%</td>
|
||||
<td data-value="42" class="abs high">36/42</td>
|
||||
<td data-value="93.75" class="pct high">93.75%</td>
|
||||
<td data-value="16" class="abs high">15/16</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="37" class="abs high">37/37</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="server.ts"><a href="server.ts.html">server.ts</a></td>
|
||||
<td data-value="44.23" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 44%"></div><div class="cover-empty" style="width: 56%"></div></div>
|
||||
<td class="file medium" data-value="server.ts"><a href="server.ts.html">server.ts</a></td>
|
||||
<td data-value="50" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 50%"></div><div class="cover-empty" style="width: 50%"></div></div>
|
||||
</td>
|
||||
<td data-value="44.23" class="pct low">44.23%</td>
|
||||
<td data-value="52" class="abs low">23/52</td>
|
||||
<td data-value="50" class="pct medium">50%</td>
|
||||
<td data-value="52" class="abs medium">26/52</td>
|
||||
<td data-value="70" class="pct medium">70%</td>
|
||||
<td data-value="10" class="abs medium">7/10</td>
|
||||
<td data-value="61.53" class="pct medium">61.53%</td>
|
||||
<td data-value="13" class="abs medium">8/13</td>
|
||||
<td data-value="43.13" class="pct low">43.13%</td>
|
||||
<td data-value="51" class="abs low">22/51</td>
|
||||
<td data-value="49.01" class="pct low">49.01%</td>
|
||||
<td data-value="51" class="abs low">25/51</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -146,7 +146,7 @@
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2026-02-06T20:43:25.074Z
|
||||
at 2026-02-06T20:50:54.063Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
|
||||
+44
-44
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">89.47% </span>
|
||||
<span class="strong">97.36% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>34/38</span>
|
||||
<span class='fraction'>37/38</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">66.66% </span>
|
||||
<span class="strong">85.71% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>28/42</span>
|
||||
<span class='fraction'>36/42</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">81.25% </span>
|
||||
<span class="strong">93.75% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>13/16</span>
|
||||
<span class='fraction'>15/16</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">91.89% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>34/37</span>
|
||||
<span class='fraction'>37/37</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -240,11 +240,11 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">23x</span>
|
||||
<span class="cline-any cline-yes">45x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -259,33 +259,33 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">18x</span>
|
||||
<span class="cline-any cline-yes">32x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">19x</span>
|
||||
<span class="cline-any cline-yes">19x</span>
|
||||
<span class="cline-any cline-yes">19x</span>
|
||||
<span class="cline-any cline-yes">19x</span>
|
||||
<span class="cline-any cline-yes">19x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -301,22 +301,22 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-yes">25x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-yes">24x</span>
|
||||
<span class="cline-any cline-yes">24x</span>
|
||||
<span class="cline-any cline-yes">25x</span>
|
||||
<span class="cline-any cline-yes">25x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -324,10 +324,10 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -407,19 +407,19 @@ export function getCommandDetail(cmd: CommandLike): string | undefined {
|
||||
case 'screenshot':
|
||||
return chalk.dim(cmd.path || 'screenshot.png');
|
||||
case 'html':
|
||||
return cmd.full ? <span class="branch-0 cbranch-no" title="branch not covered" >chalk.dim('(full)') : u</span>ndefined;
|
||||
return cmd.full ? chalk.dim('(full)') : undefined;
|
||||
case 'wait':
|
||||
return chalk.dim(`${cmd.ms || <span class="branch-1 cbranch-no" title="branch not covered" >1000}</span>ms`);
|
||||
<span class="branch-7 cbranch-no" title="branch not covered" > case 'eval':</span>
|
||||
<span class="cstat-no" title="statement not covered" > return chalk.dim(truncate(cmd.script || '', 50));</span>
|
||||
return chalk.dim(`${cmd.ms || 1000}ms`);
|
||||
case 'eval':
|
||||
return chalk.dim(truncate(cmd.script || <span class="branch-1 cbranch-no" title="branch not covered" >'', 5</span>0));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatCommand(cmd: CommandLike): string {
|
||||
const color = cmdColor[cmd.cmd] || <span class="branch-1 cbranch-no" title="branch not covered" >chalk.white;</span>
|
||||
const icon = icons[cmd.cmd] || <span class="branch-1 cbranch-no" title="branch not covered" >'•';</span>
|
||||
const color = cmdColor[cmd.cmd] || chalk.white;
|
||||
const icon = icons[cmd.cmd] || '•';
|
||||
const detail = getCommandDetail(cmd);
|
||||
const suffix = detail ? ` ${detail}` : '';
|
||||
return `${ts()} ${chalk.bold(color(icon))} ${color(cmd.cmd.toUpperCase())}${suffix}`;
|
||||
@@ -440,10 +440,10 @@ const resultFormatters: Record<string, (r: ResultLike) => string | undefin
|
||||
goto: (r) => r.title,
|
||||
click: (r) => (r.url ? `→ ${r.url}` : <span class="branch-1 cbranch-no" title="branch not covered" >undefined),</span>
|
||||
query: (r) => (r.count !== undefined ? `Found ${r.count} element(s)` : <span class="branch-1 cbranch-no" title="branch not covered" >undefined),</span>
|
||||
screenshot: <span class="fstat-no" title="function not covered" >(r</span>) => (<span class="cstat-no" title="statement not covered" >r.path ? `Saved to ${r.path}` : undefined),</span>
|
||||
screenshot: (r) => (r.path ? `Saved to ${r.path}` : <span class="branch-1 cbranch-no" title="branch not covered" >undefined),</span>
|
||||
url: (r) => r.url,
|
||||
html: (r) => (r.html !== undefined ? `${r.html.length} chars` : <span class="branch-1 cbranch-no" title="branch not covered" >undefined),</span>
|
||||
eval: <span class="fstat-no" title="function not covered" >(r</span>) => (<span class="cstat-no" title="statement not covered" >r.result !== undefined ? truncate(JSON.stringify(r.result), 80) : undefined),</span>
|
||||
eval: (r) => (r.result !== undefined ? truncate(JSON.stringify(r.result), 80) : <span class="branch-1 cbranch-no" title="branch not covered" >undefined),</span>
|
||||
};
|
||||
|
||||
export function formatResult(cmd: CommandLike, result: ResultLike): string {
|
||||
@@ -481,7 +481,7 @@ export const stderrLogger = createLogger(<span class="fstat-no" title="function
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2026-02-06T20:43:25.074Z
|
||||
at 2026-02-06T20:50:54.063Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
|
||||
+21
-21
@@ -23,9 +23,9 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">44.23% </span>
|
||||
<span class="strong">50% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>23/52</span>
|
||||
<span class='fraction'>26/52</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -44,9 +44,9 @@
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">43.13% </span>
|
||||
<span class="strong">49.01% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>22/51</span>
|
||||
<span class='fraction'>25/51</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line medium'></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
@@ -225,28 +225,28 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -276,7 +276,7 @@
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -355,9 +355,9 @@ export class BrowserServer {
|
||||
logger.result(cmd, result);
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
const error = (<span class="cstat-no" title="statement not covered" >err as Error).message;</span>
|
||||
<span class="cstat-no" title="statement not covered" > console.log(`${ts()} ${logSymbols.error} ${chalk.red(error)}`);</span>
|
||||
<span class="cstat-no" title="statement not covered" > res.status(500).json({ ok: false, error });</span>
|
||||
const error = (err as Error).message;
|
||||
console.log(`${ts()} ${logSymbols.error} ${chalk.red(error)}`);
|
||||
res.status(500).json({ ok: false, error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ export async function <span class="fstat-no" title="function not covered" >start
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2026-02-06T20:43:25.074Z
|
||||
at 2026-02-06T20:50:54.063Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -409,7 +409,7 @@ export type CommandResponse = SuccessResponse | ErrorResponse;
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2026-02-06T20:43:25.074Z
|
||||
at 2026-02-06T20:50:54.063Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
|
||||
+4
-1
@@ -21,8 +21,11 @@
|
||||
"fix": "biome check --write src/",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test:unit": "vitest run --coverage --passWithNoTests",
|
||||
"test:integration": "vitest run --coverage --config vitest.integration.config.ts",
|
||||
"test:watch": "vitest",
|
||||
"test": "npm run fix && npm run typecheck && npm run test:unit && npm run build",
|
||||
"test:all": "vitest run --coverage --config vitest.all.config.ts",
|
||||
"test": "npm run fix && npm run typecheck && npm run test:all && npm run build",
|
||||
"coverage": "npm run test:all && open coverage/index.html",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
import { ClaudeBrowser } from './browser.js';
|
||||
|
||||
describe('ClaudeBrowser Integration', () => {
|
||||
let browser: ClaudeBrowser;
|
||||
|
||||
beforeAll(async () => {
|
||||
browser = new ClaudeBrowser({ headless: true });
|
||||
await browser.launch();
|
||||
}, 30000);
|
||||
|
||||
afterAll(async () => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
describe('navigation', () => {
|
||||
it('navigates to a URL and returns title', async () => {
|
||||
const result = await browser.goto('https://example.com');
|
||||
expect(result.url).toContain('example.com');
|
||||
expect(result.title).toBe('Example Domain');
|
||||
});
|
||||
|
||||
it('gets current URL and title', async () => {
|
||||
const result = await browser.getUrl();
|
||||
expect(result.url).toContain('example.com');
|
||||
expect(result.title).toBe('Example Domain');
|
||||
});
|
||||
|
||||
it('gets page HTML', async () => {
|
||||
const html = await browser.getHtml();
|
||||
expect(html.toLowerCase()).toContain('<!doctype html>');
|
||||
expect(html).toContain('Example Domain');
|
||||
});
|
||||
|
||||
it('gets full page HTML', async () => {
|
||||
const html = await browser.getHtml(true);
|
||||
expect(html.toLowerCase()).toContain('<!doctype html>');
|
||||
expect(html.length).toBeGreaterThan(100);
|
||||
});
|
||||
|
||||
it('reloads the page', async () => {
|
||||
const result = await browser.reload();
|
||||
expect(result.url).toContain('example.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('DOM interaction', () => {
|
||||
it('queries elements', async () => {
|
||||
const elements = await browser.query('h1');
|
||||
expect(elements.length).toBe(1);
|
||||
expect(elements[0].tag).toBe('h1');
|
||||
expect(elements[0].text).toContain('Example Domain');
|
||||
});
|
||||
|
||||
it('queries multiple elements', async () => {
|
||||
const elements = await browser.query('p');
|
||||
expect(elements.length).toBeGreaterThan(0);
|
||||
expect(elements[0].tag).toBe('p');
|
||||
});
|
||||
|
||||
it('clicks an element', async () => {
|
||||
await browser.goto('https://example.com');
|
||||
const result = await browser.click('a');
|
||||
expect(result.url).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('screenshots', () => {
|
||||
it('takes a screenshot', async () => {
|
||||
await browser.goto('https://example.com');
|
||||
const result = await browser.screenshot('screenshots/test-integration.png');
|
||||
expect(result.path).toContain('test-integration.png');
|
||||
expect(result.buffer).toBeDefined();
|
||||
});
|
||||
|
||||
it('takes a full page screenshot', async () => {
|
||||
const result = await browser.screenshot('screenshots/test-full.png', true);
|
||||
expect(result.path).toContain('test-full.png');
|
||||
});
|
||||
});
|
||||
|
||||
describe('wait', () => {
|
||||
it('waits for specified time', async () => {
|
||||
const start = Date.now();
|
||||
await browser.wait(100);
|
||||
const elapsed = Date.now() - start;
|
||||
expect(elapsed).toBeGreaterThanOrEqual(90);
|
||||
});
|
||||
});
|
||||
|
||||
describe('eval', () => {
|
||||
it('evaluates JavaScript', async () => {
|
||||
await browser.goto('https://example.com');
|
||||
const result = await browser.eval('document.title');
|
||||
expect(result).toBe('Example Domain');
|
||||
});
|
||||
|
||||
it('evaluates expressions', async () => {
|
||||
const result = await browser.eval('1 + 1');
|
||||
expect(result).toBe(2);
|
||||
});
|
||||
|
||||
it('evaluates complex expressions', async () => {
|
||||
const result = await browser.eval('document.querySelectorAll("p").length');
|
||||
expect(result).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pages', () => {
|
||||
it('creates a new page', async () => {
|
||||
await browser.newPage();
|
||||
const result = await browser.getUrl();
|
||||
expect(result.url).toBe('about:blank');
|
||||
});
|
||||
|
||||
it('navigates in new page', async () => {
|
||||
const result = await browser.goto('https://example.com');
|
||||
expect(result.title).toBe('Example Domain');
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeCommand', () => {
|
||||
it('handles goto command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'goto', url: 'https://example.com' });
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.title).toBe('Example Domain');
|
||||
}
|
||||
});
|
||||
|
||||
it('handles query command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'query', selector: 'h1' });
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.count).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
it('handles url command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'url' });
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.url).toContain('example.com');
|
||||
}
|
||||
});
|
||||
|
||||
it('handles html command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'html' });
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.html).toContain('Example');
|
||||
}
|
||||
});
|
||||
|
||||
it('handles wait command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'wait', ms: 50 });
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles eval command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'eval', script: '2+2' });
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.result).toBe(4);
|
||||
}
|
||||
});
|
||||
|
||||
it('handles screenshot command', async () => {
|
||||
const result = await browser.executeCommand({
|
||||
cmd: 'screenshot',
|
||||
path: 'screenshots/cmd-test.png',
|
||||
});
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles reload command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'reload' });
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles click command', async () => {
|
||||
await browser.executeCommand({ cmd: 'goto', url: 'https://example.com' });
|
||||
const result = await browser.executeCommand({ cmd: 'click', selector: 'a' });
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles newpage command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'newpage' });
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles back command', async () => {
|
||||
await browser.executeCommand({ cmd: 'goto', url: 'https://example.com' });
|
||||
const result = await browser.executeCommand({ cmd: 'back' });
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles forward command', async () => {
|
||||
const result = await browser.executeCommand({ cmd: 'forward' });
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -66,6 +66,36 @@ describe('getCommandDetail', () => {
|
||||
const result = getCommandDetail({ cmd: 'wait', ms: 500 });
|
||||
expect(result).toContain('500');
|
||||
});
|
||||
|
||||
it('returns default ms for wait command without ms', () => {
|
||||
const result = getCommandDetail({ cmd: 'wait' });
|
||||
expect(result).toContain('1000');
|
||||
});
|
||||
|
||||
it('returns script for eval command', () => {
|
||||
const result = getCommandDetail({ cmd: 'eval', script: 'document.title' });
|
||||
expect(result).toContain('document.title');
|
||||
});
|
||||
|
||||
it('truncates long eval scripts', () => {
|
||||
const longScript = 'a'.repeat(100);
|
||||
const result = getCommandDetail({ cmd: 'eval', script: longScript });
|
||||
expect(result).toContain('...');
|
||||
});
|
||||
|
||||
it('returns (full) for html command with full=true', () => {
|
||||
const result = getCommandDetail({ cmd: 'html', full: true });
|
||||
expect(result).toContain('full');
|
||||
});
|
||||
|
||||
it('returns undefined for html command with full=false', () => {
|
||||
expect(getCommandDetail({ cmd: 'html', full: false })).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns selector for query command', () => {
|
||||
const result = getCommandDetail({ cmd: 'query', selector: '.items' });
|
||||
expect(result).toContain('.items');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatCommand', () => {
|
||||
@@ -97,6 +127,37 @@ describe('formatResult', () => {
|
||||
const result = formatResult({ cmd: 'query' }, { ok: true, count: 5 });
|
||||
expect(result).toContain('5');
|
||||
});
|
||||
|
||||
it('formats click result with url', () => {
|
||||
const result = formatResult({ cmd: 'click' }, { ok: true, url: '/page' });
|
||||
expect(result).toContain('/page');
|
||||
});
|
||||
|
||||
it('formats screenshot result with path', () => {
|
||||
const result = formatResult({ cmd: 'screenshot' }, { ok: true, path: 'test.png' });
|
||||
expect(result).toContain('Saved to test.png');
|
||||
});
|
||||
|
||||
it('formats url result', () => {
|
||||
const result = formatResult({ cmd: 'url' }, { ok: true, url: 'https://example.com' });
|
||||
expect(result).toContain('https://example.com');
|
||||
});
|
||||
|
||||
it('formats html result with length', () => {
|
||||
const result = formatResult({ cmd: 'html' }, { ok: true, html: '<html></html>' });
|
||||
expect(result).toContain('13 chars');
|
||||
});
|
||||
|
||||
it('formats eval result', () => {
|
||||
const result = formatResult({ cmd: 'eval' }, { ok: true, result: { foo: 'bar' } });
|
||||
expect(result).toContain('foo');
|
||||
expect(result).toContain('bar');
|
||||
});
|
||||
|
||||
it('formats result without formatter', () => {
|
||||
const result = formatResult({ cmd: 'wait' }, { ok: true });
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('createLogger', () => {
|
||||
|
||||
+69
-1
@@ -7,7 +7,7 @@ vi.mock('./browser.js', () => ({
|
||||
ClaudeBrowser: class MockClaudeBrowser {
|
||||
async launch() {}
|
||||
async close() {}
|
||||
async executeCommand(cmd: { cmd: string; url?: string }) {
|
||||
async executeCommand(cmd: { cmd: string; url?: string; path?: string }) {
|
||||
switch (cmd.cmd) {
|
||||
case 'goto':
|
||||
return { ok: true, url: cmd.url, title: 'Test Page' };
|
||||
@@ -21,6 +21,22 @@ vi.mock('./browser.js', () => ({
|
||||
return { ok: true, url: 'https://example.com', title: 'Example' };
|
||||
case 'html':
|
||||
return { ok: true, html: '<html></html>' };
|
||||
case 'back':
|
||||
return { ok: true, url: '/previous' };
|
||||
case 'forward':
|
||||
return { ok: true, url: '/next' };
|
||||
case 'reload':
|
||||
return { ok: true, url: '/current' };
|
||||
case 'wait':
|
||||
return { ok: true };
|
||||
case 'screenshot':
|
||||
return { ok: true, path: cmd.path || 'screenshot.png' };
|
||||
case 'eval':
|
||||
return { ok: true, result: 2 };
|
||||
case 'newpage':
|
||||
return { ok: true };
|
||||
case 'error':
|
||||
throw new Error('Test error');
|
||||
default:
|
||||
return { ok: true };
|
||||
}
|
||||
@@ -119,5 +135,57 @@ describe('BrowserServer', () => {
|
||||
|
||||
expect(res.body.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles back command', async () => {
|
||||
const app = server.getApp();
|
||||
const res = await request(app).post('/').send({ cmd: 'back' }).expect(200);
|
||||
expect(res.body.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles forward command', async () => {
|
||||
const app = server.getApp();
|
||||
const res = await request(app).post('/').send({ cmd: 'forward' }).expect(200);
|
||||
expect(res.body.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles reload command', async () => {
|
||||
const app = server.getApp();
|
||||
const res = await request(app).post('/').send({ cmd: 'reload' }).expect(200);
|
||||
expect(res.body.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles wait command', async () => {
|
||||
const app = server.getApp();
|
||||
const res = await request(app).post('/').send({ cmd: 'wait', ms: 100 }).expect(200);
|
||||
expect(res.body.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles screenshot command', async () => {
|
||||
const app = server.getApp();
|
||||
const res = await request(app)
|
||||
.post('/')
|
||||
.send({ cmd: 'screenshot', path: 'test.png' })
|
||||
.expect(200);
|
||||
expect(res.body.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles eval command', async () => {
|
||||
const app = server.getApp();
|
||||
const res = await request(app).post('/').send({ cmd: 'eval', script: '1+1' }).expect(200);
|
||||
expect(res.body.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles newpage command', async () => {
|
||||
const app = server.getApp();
|
||||
const res = await request(app).post('/').send({ cmd: 'newpage' }).expect(200);
|
||||
expect(res.body.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('handles errors and returns 500', async () => {
|
||||
const app = server.getApp();
|
||||
const res = await request(app).post('/').send({ cmd: 'error' }).expect(500);
|
||||
expect(res.body.ok).toBe(false);
|
||||
expect(res.body.error).toBe('Test error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['src/**/*.test.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['src/**/*.test.ts', 'src/cli.ts', 'src/mcp.ts', 'src/index.ts'],
|
||||
reporter: ['text', 'html'],
|
||||
reportsDirectory: './coverage',
|
||||
},
|
||||
testTimeout: 60000,
|
||||
hookTimeout: 60000,
|
||||
},
|
||||
});
|
||||
@@ -3,10 +3,14 @@ import { defineConfig } from 'vitest/config';
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['src/**/*.test.ts'],
|
||||
exclude: ['src/**/*.integration.test.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['src/**/*.test.ts', 'src/cli.ts', 'src/mcp.ts', 'src/index.ts'],
|
||||
reporter: ['text', 'html'],
|
||||
reportsDirectory: './coverage',
|
||||
},
|
||||
testTimeout: 30000,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['src/**/*.integration.test.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['src/**/*.test.ts', 'src/cli.ts', 'src/mcp.ts', 'src/index.ts'],
|
||||
reporter: ['text', 'html'],
|
||||
reportsDirectory: './coverage',
|
||||
},
|
||||
testTimeout: 60000,
|
||||
hookTimeout: 60000,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user