Skip to content

Conversation

@ignatandrei
Copy link
Owner

@ignatandrei ignatandrei commented Nov 22, 2025

Summary by CodeRabbit

  • New Features

    • JavaScript extensions framework with Aspire-based orchestration, Blazor web UI (weather page, pages/layout), and sample JavaScript app
    • Execute npm scripts from the UI with real-time console logs
  • Tests

    • Large Playwright test suite and helpers for end-to-end and unit flows, plus test plans and generation guides
  • Documentation

    • Test planning/generator/healer guides and package README improvements
  • Chores

    • Upgraded projects to .NET 10 and updated package metadata/tags

✏️ Tip: You can customize this high-level summary in your review settings.

Introduces a new JavaScriptExtensions/GenerateTests project with Playwright-based test generation, planning, and healing agents. Adds configuration, test plans, example tests, and supporting files for automated browser testing. Updates AspireResourceExtensions documentation and tags. No changes to application runtime logic.
Copilot AI review requested due to automatic review settings November 22, 2025 14:38
@coderabbitai
Copy link

coderabbitai bot commented Nov 22, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a JavaScript testing ecosystem and multiple Aspire .NET projects: Playwright test scaffolding and specs, JavaScript extension libraries and app host, Blazor web frontend, shared service defaults, SQL Server project updates to net10.0, and an API change to StartParsing(app) → StartParsing(app, builder).

Changes

Cohort / File(s) Summary
AspireResourceExtensions metadata & docs
src/AspireResourceExtensions/AspireResourceExtensionsAspire/AspireResourceExtensionsAspire.csproj, src/AspireResourceExtensions/README.md
Added PackageTags ("integrations","hosting"); updated README and example to reflect StartParsing(app, builder) signature change.
Playwright GenerateTests project (config & tooling)
src/JavaScriptExtensions/GenerateTests/package.json, src/JavaScriptExtensions/GenerateTests/playwright.config.ts, src/JavaScriptExtensions/GenerateTests/.gitignore, src/JavaScriptExtensions/GenerateTests/.vscode/mcp.json, src/JavaScriptExtensions/GenerateTests/tests/config.json
New test project scaffolding: npm scripts, Playwright config, ignore rules, MCP server config, and test config (recordVideo).
Playwright helpers & utilities
src/JavaScriptExtensions/GenerateTests/tests/common.ts, src/JavaScriptExtensions/GenerateTests/tests/showMessage.js
Added shared test fixtures, video handling, navigation helpers, anyVisible, sleep utilities, and DOM overlay helper for video messages.
Playwright test specs
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/*.spec.ts, src/JavaScriptExtensions/GenerateTests/tests/DemoTests/packageToCommands.spec.ts, src/DocumentorDatabaseExtensions/GenerateTest/tests/DemoTests/demoVideoDocumentor.spec.ts
Added multiple UI tests covering resource actions, command execution (test/dev/build/start/restart), pause/resume log streaming, multi-command flows, resource details, and small formatting edit in an existing demo test.
Playwright chatmode agents & test plan
src/JavaScriptExtensions/GenerateTests/.github/chatmodes/🎭 planner.chatmode.md, .../generator.chatmode.md, .../healer.chatmode.md, src/JavaScriptExtensions/GenerateTests/test-plan-console-logs-commands.md
Added three agent spec docs for planning/generating/healing Playwright tests and a 20-scenario test plan for console logs/commands.
JavaScriptExtensions solution & sample
src/JavaScriptExtensions/JavaScriptExtensions.slnx, src/JavaScriptExtensions/LICENSE, src/JavaScriptExtensions/README.md, src/JavaScriptExtensions/SampleJavascript/package.json
New solution file, MIT license, README, and sample package.json with npm scripts.
JavaScriptExtensions.Aspire library
src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptExtensionsAspire.csproj, src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs
New library and extension method AddNpmCommandsFromPackage() that reads package.json, registers npm script commands, and runs processes with cross-platform exe resolution and async output consumption.
JavaScriptExtensions.ApiService (minimal API)
src/JavaScriptExtensions/JavaScriptExtensions.ApiService/*.csproj, .../Program.cs, .../JavaScriptExtensions.ApiService.http, .../Properties/launchSettings.json, .../appsettings*.json
New ASP.NET Core minimal API project with weather endpoint, OpenAPI, health/logging configs and launch profiles.
JavaScriptExtensions.Web (Blazor)
src/JavaScriptExtensions/JavaScriptExtensions.Web/JavaScriptExtensions.Web.csproj, .../Program.cs, .../WeatherApiClient.cs, .../Components/*, .../wwwroot/app.css, .../appsettings*.json, .../Properties/launchSettings.json
New Blazor app with pages (Home, Counter, Weather, Error), layout (MainLayout, NavMenu), routing, client for Weather API, styling, and app configuration.
JavaScriptExtensions.ServiceDefaults
src/JavaScriptExtensions/JavaScriptExtensions.ServiceDefaults/JavaScriptExtensions.ServiceDefaults.csproj, .../Extensions.cs
New shared library adding AddServiceDefaults and OpenTelemetry/health check/service discovery/HTTP resilience wiring.
JavaScriptExtensions.AppHost
src/JavaScriptExtensions/JavaScriptExtensions.AppHost/JavaScriptExtensions.AppHost.csproj, .../AppHost.cs, .../Properties/launchSettings.json, .../appsettings*.json
New AppHost orchestrating API/Web/JavaScript apps and tests; demonstrates builder usage and calls StartParsing(app, builder) in startup sequence.
SQL Server extensions updates
src/SqlServerExtensions/.../*.csproj, src/SqlServerExtensions/SqlServerExtensions.AppHost/AppHost.cs
Bumped projects from net9.0 → net10.0, updated package versions (multiple Microsoft.* and Aspire.Hosting.*), replaced NodeJs host with Aspire.Hosting.JavaScript, changed AddNpmApp → AddJavaScriptApp and added .WithExplicitStart() in AppHost.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Builder as Builder
    participant App as App (built)
    participant AppHost as AppHost.cs
    participant Parser as StartParsing
    participant Services as Services (API/Web/JS)

    Builder->>App: Build()
    AppHost->>Parser: StartParsing(App, Builder)
    Parser->>Services: Register resources & start parsing
    par Running services
        Services->>Services: API ListenAsync()
        Services->>Services: Web ListenAsync()
        Services->>Services: JS apps RunAsync()
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas to focus review on:

  • src/JavaScriptExtensions/JavaScriptExtensions.ServiceDefaults/Extensions.cs — OpenTelemetry, exporter selection, health endpoint mapping.
  • src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs — process execution, timeout/cancellation, cross-platform exe resolution, output handling.
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/WeatherApiClient.cs and Playwright tests (tests/common.ts) — async streaming, cancellation, and test timing/wait strategies.
  • AppHost usage sites referencing StartParsing(app, builder) to ensure correct new-call pattern.

Possibly related PRs

Poem

🐰 I dug a tunnel through code today,

Tests and Blazor chips all in a row,
Packages sprouted commands that play,
Parsers hum and weather breezes blow,
A tiny rabbit cheers: "Build, run, and go!"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Add Playwright test generation and planning infrastructure' accurately summarizes the main changes across the changeset, which introduces comprehensive Playwright test infrastructure including test generation chatmodes, planning documentation, configuration files, test utilities, and sample test specs.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Javascript

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds Playwright test generation and planning infrastructure by introducing JavaScript extension capabilities for .NET Aspire and upgrading several project dependencies to future versions.

Key Changes

  • Upgrade target framework from net9.0 to net10.0 across multiple projects
  • Transition from AddNpmApp to AddJavaScriptApp in AppHost configuration
  • Add new JavaScriptExtensionsAspire library with package.json command integration
  • Update Aspire and Microsoft package dependencies to version 13.0.0 and 10.0.0 respectively

Reviewed changes

Copilot reviewed 73 out of 121 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
SqlServerExtensions.Tests.csproj Updates target framework to net10.0 and upgrades test-related packages
SqlServerExtensions.ServiceDefaults.csproj Updates framework and Microsoft Extensions packages to 10.0.0
SqlServerExtensions.AppHost.csproj Replaces NodeJs hosting with JavaScript hosting, updates Aspire packages
AppHost.cs Changes from AddNpmApp to AddJavaScriptApp method
SqlServerExtensions.ApiService.csproj Updates framework and OpenAPI package version
SqlExtensionsAspire.csproj Updates framework and version numbering
DataProject.csproj Updates dependency injection abstractions package
JavaScriptExtensionsAspire/* New library for JavaScript/npm integration with Aspire
SampleJavascript/* New sample JavaScript project with package.json
JavaScriptExtensions.Web/* New web project with Bootstrap 5.3.3 assets
Files not reviewed (2)
  • src/JavaScriptExtensions/GenerateTests/package-lock.json: Language not supported
  • src/JavaScriptExtensions/SampleJavascript/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -0,0 +1,21 @@
MIT License

Copyright (c)2024 Ignat Andrei
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after Copyright (c) - should be Copyright (c) 2024 instead of Copyright (c)2024.

Copilot uses AI. Check for mistakes.
```


See the how it looks in the
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete sentence: "See the how it looks in the" should be completed or removed. The sentence appears to be cut off before the link.

Copilot uses AI. Check for mistakes.
if (nr == 0) nr = int.MinValue;
if (string.IsNullOrWhiteSpace(resultError))
{
resultError = "No error message provided.See previous messages";
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after . in the error message. Should be No error message provided. See previous messages instead of No error message provided.See previous messages.

Copilot uses AI. Check for mistakes.
}
private static async Task<ExecuteProcessResult> ExecuteProcess(ProcessStartInfo exportStartInfo)
{
var exportProcess = new Process { StartInfo = exportStartInfo };
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disposable 'Process' is created but not disposed.

Copilot uses AI. Check for mistakes.
{
var builder = appResource.ApplicationBuilder;
var wd = appResource.Resource.WorkingDirectory;
var npm = appResource.Resource.Command;
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to npm is useless, since its value is never read.

Copilot uses AI. Check for mistakes.
}

var timeout = TimeSpan.FromMinutes(5);
var exited = exportProcess.WaitForExit(timeout);
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to exited is useless, since its value is never read.

Copilot uses AI. Check for mistakes.
exportProcess.Kill(true);
}
int nr = exportProcess.ExitCode;
if (nr == 0) nr = int.MinValue;
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to nr is useless, since its value is never read.

Copilot uses AI. Check for mistakes.
// seed: tests/seed.spec.ts

//import { expect } from '@playwright/test';
import { test,LOGIN, BASE, sleep, flashAndClick, sleepMessage, setupFirstPage, endTest } from '../common';
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused imports LOGIN, endTest.

Suggested change
import { test,LOGIN, BASE, sleep, flashAndClick, sleepMessage, setupFirstPage, endTest } from '../common';
import { test, BASE, sleep, flashAndClick, sleepMessage, setupFirstPage } from '../common';

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,7 @@
import { test, expect } from '@playwright/test';
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import expect.

Suggested change
import { test, expect } from '@playwright/test';
import { test } from '@playwright/test';

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,140 @@
import { Page,test as baseTest, expect, Locator, TestInfo } from '@playwright/test';
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import expect.

Suggested change
import { Page,test as baseTest, expect, Locator, TestInfo } from '@playwright/test';
import { Page,test as baseTest, Locator, TestInfo } from '@playwright/test';

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 25

🧹 Nitpick comments (28)
src/DocumentorDatabaseExtensions/GenerateTest/tests/DemoTests/demoVideoDocumentor.spec.ts (2)

16-23: Formatting change approved; however, address variable shadowing.

The extra blank line on line 18 is a minor formatting addition with no functional impact. However, the test function parameter named test: TestInfo on line 16 shadows the test function imported from ../common on line 2, which can cause confusion during code reading and maintenance.

Rename the parameter from test to testInfo to avoid shadowing the imported test function:

- test('GenerateDocumentation', async ({ page }, test:TestInfo) => {
-    
+ test('GenerateDocumentation', async ({ page }, testInfo: TestInfo) => {
+    
-    await sleepMessage(page,`<h1>Starting demo for ${test.title}</h1>
+    await sleepMessage(page,`<h1>Starting demo for ${testInfo.title}</h1>

Then update all references to test parameter within the function body (lines 19 and 40):

   await sleepMessage(page,`<h1>Starting demo for ${test.title}</h1>
   ...
   await sleepMessage(page,`<h1>End demo for ${test.title}</h1>

should become:

   await sleepMessage(page,`<h1>Starting demo for ${testInfo.title}</h1>
   ...
   await sleepMessage(page,`<h1>End demo for ${testInfo.title}</h1>

5-10: Remove or document commented-out code.

Lines 6–7 and 9 contain commented-out code that appears to be debug or obsolete. Consider removing these lines if they are no longer needed, or convert to a proper TODO comment if they document a future consideration.

   test.beforeEach(async ({ page,browser },testInfo) => {
-      //var cnt = await browser.newContext();
-      //var newPage = await cnt.newPage();
       await setupFirstPage(page,testInfo);
-       //await cnt.close();
src/JavaScriptExtensions/SampleJavascript/package.json (2)

2-4: Fill in package metadata for clarity.

Empty description, author, and keywords fields reduce discoverability and clarity for future maintainers. While not a functional issue, consider providing meaningful values:

   "name": "samplejavascript",
   "version": "1.0.0",
-  "description": "",
+  "description": "Sample JavaScript project demonstrating test generation and Playwright integration",
-  "keywords": [],
+  "keywords": ["aspire", "playwright", "tests", "javascript"],
-  "author": "",
+  "author": "Your Name or Organization",

Also applies to: 12-13


15-15: The type: "commonjs" field is redundant.

The default module type in Node.js is CommonJS, so explicitly setting "type": "commonjs" is unnecessary. This can be safely removed, though keeping it is harmless.

-  "type": "commonjs"

Alternatively, if module type clarity is valuable for documentation, you can keep it.

src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Layout/NavMenu.razor (1)

7-9: Consider improving accessibility of the navigation toggle mechanism.

The current pattern uses a checkbox toggler with an onclick handler on a div to programmatically trigger the checkbox. While functional, this approach has accessibility limitations. Consider using a proper <button> element with Blazor's @onclick directive for better keyboard navigation and screen reader support.

Alternative implementation:

-<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
-
-<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
+@code {
+    private bool navMenuExpanded = false;
+    private void ToggleNavMenu() => navMenuExpanded = !navMenuExpanded;
+}
+
+<button class="navbar-toggler" @onclick="ToggleNavMenu" aria-label="Toggle navigation">
+    <span class="navbar-toggler-icon"></span>
+</button>
+
+<div class="nav-scrollable @(navMenuExpanded ? "show" : "")">

Then update the CSS to use the show class instead of the :checked pseudo-class.

src/JavaScriptExtensions/JavaScriptExtensions.AppHost/appsettings.Development.json (1)

1-8: Consider enabling more verbose DCP logging for Development.

The Development configuration omits the Aspire.Hosting.Dcp log level setting present in the base appsettings.json. While the base Warning level will apply, you might want to set it to Information or Debug in Development to aid in troubleshooting Aspire hosting issues.

 {
   "Logging": {
     "LogLevel": {
       "Default": "Information",
-      "Microsoft.AspNetCore": "Warning"
+      "Microsoft.AspNetCore": "Warning",
+      "Aspire.Hosting.Dcp": "Information"
     }
   }
 }
src/JavaScriptExtensions/JavaScriptExtensions.Web/appsettings.json (1)

1-9: Base appsettings match typical ASP.NET Core defaults; consider tightening AllowedHosts in production

Logging levels mirror the usual template defaults and are fine. For production deployments, you may eventually want to replace AllowedHosts": "*" with your expected hostnames, but this is perfectly acceptable for dev/sample usage.

src/AspireResourceExtensions/README.md (1)

53-55: Usage snippet correctly reflects new StartParsing(app, builder) API

The updated example builds once, then calls StartParsing(app, builder) and awaits both app.RunAsync() and the parsing task, which matches the described behavior. As a tiny polish, you could:

  • Add a space after the comma: StartParsing(app, builder).
  • Either drop the null-forgiving ! (aspire.Resource...) or use it consistently in earlier examples too, for visual consistency.
src/JavaScriptExtensions/JavaScriptExtensions.AppHost/AppHost.cs (2)

26-30: Align nullability handling of aspire

You call aspire.Resource.AddEnvironmentVariablesTo(jsTests); without null-forgiveness, but later use aspire!.Resource.StartParsing(app, builder);. If builder.AddAspireResource() is guaranteed non-null in this context, it would be cleaner to:

  • Declare aspire as non-nullable and drop the !, or
  • Use the same nullability approach consistently at both call sites.

This keeps the intent clear and avoids mixed null-safety signals.


17-24: Consider explicitly ordering jsTests relative to the Aspire resource

Given that jsTests likely uses the Aspire URLs injected via AddEnvironmentVariablesTo(jsTests), you may want to ensure it doesn’t start before Aspire is fully ready (similar to the README Node/Playwright examples that use WaitFor(aspire) and WithExplicitStart()).

If AddJavaScriptApp supports it in the same way as project resources, consider something along the lines of:

var jsTests = builder.AddJavaScriptApp("Tests", "../GenerateTests")
    .AddNpmCommandsFromPackage()
    // .WaitFor(aspire)
    // .WithExplicitStart()
    ;

so the test execution is clearly sequenced after the Aspire resource is up.

src/JavaScriptExtensions/JavaScriptExtensions.Web/WeatherApiClient.cs (1)

3-29: Weather client logic looks good; consider sharing the WeatherForecast contract

The streaming fetch with a configurable maxItems and an empty‑array fallback is sound and should interoperate correctly with the API’s /weatherforecast endpoint.

You have a very similar WeatherForecast record defined in the API project; consider moving this contract into a small shared library (or a shared file) to avoid drift between client and server over time. If you keep them separate on purpose, just verify both definitions stay in sync whenever the API model changes.

src/JavaScriptExtensions/JavaScriptExtensions.ServiceDefaults/Extensions.cs (2)

47-79: OpenTelemetry setup is solid; consider explicit resource configuration

The metrics/tracing setup and health-endpoint filtering look correct and idiomatic. One optional enhancement is to explicitly configure resource attributes (e.g., service.name, deployment.environment) on the OpenTelemetry builder so these services are easier to distinguish and correlate across environments and backends, rather than relying only on builder.Environment.ApplicationName and library defaults.

If you adopt this, verify the exact APIs for resource configuration for your OpenTelemetry package versions (e.g., ConfigureResource(resource => resource.AddService(...))), as signatures can vary slightly between versions.


100-126: Health checks and dev-only endpoints are well-structured; mind production monitoring

The self “live” check and readiness/liveness split (/health vs /alive with tag-based filtering) are implemented correctly, and restricting these endpoints to IsDevelopment() is a reasonable default from a security standpoint. Just ensure you have an alternative strategy (Kubernetes/container probes, gateway checks, etc.) for health monitoring in test/prod, since these endpoints won’t be exposed there by default.

src/JavaScriptExtensions/GenerateTests/.gitignore (1)

2-8: Ignore recorded video artifacts as well

Given recordVideo writes to a local videos/ folder via the custom Playwright fixture, it’s worth ignoring that directory here to avoid committing large binary test artifacts.

For example:

 # Playwright
 node_modules/
 /test-results/
 /playwright-report/
 /blob-report/
 /playwright/.cache/
 /playwright/.auth/
+videos/
src/JavaScriptExtensions/GenerateTests/package.json (1)

1-20: Consider making the package private and adding a standard test script

This looks like a workspace/test-only package rather than something meant for npm. Marking it private and wiring a test script makes it safer and easier to use:

 {
   "name": "generatetests",
   "version": "1.0.0",
+  "private": true,
   "description": "",
   "main": "index.js",
   "scripts": {
-    "dev": "echo see other scripts",
-    "ui":"npx playwright test --ui",
+    "dev": "echo see other scripts",
+    "test": "npx playwright test",
+    "ui": "npx playwright test --ui",
     "smokeTest": "echo not yet implemented",
-    "video":"echo not yet implemented"
- },
+    "video": "echo not yet implemented"
+  },
   "keywords": [],
src/JavaScriptExtensions/GenerateTests/tests/config.json (1)

1-3: Confirm that always-on video recording is desired

With "recordVideo": true, every test run will produce videos, which is excellent for debugging but can be expensive (disk + time), especially in CI.

Consider one of these patterns:

  • Default to false here and enable video only when needed (e.g., via a separate config file or env flag your shouldRecordVideo helper reads).
  • Or keep true but ensure CI pipelines clean up / ignore videos/ appropriately.
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/seed.spec.ts (1)

1-7: Align seed spec with shared Playwright fixture (if intended)

Right now the seed test uses the base Playwright test from @playwright/test, while you’ve defined a custom extended test in tests/common.ts (adds video handling and showMessage.js).

If this seed is meant as the template for generated/hand-written tests, consider importing from the shared fixture instead:

-import { test, expect } from '@playwright/test';
+import { test, expect } from '../common';

(Adjust the relative path if needed.)

src/JavaScriptExtensions/GenerateTests/tests/UnitTests/clear-console-log-data.spec.ts (2)

8-13: Centralize login and console logs URLs

The tokenized login URL and console logs path are hard-coded here; given you already have shared helpers/constants in tests/common.ts, it would be more maintainable to pull these from a single place so environment changes don’t require editing every spec.


20-22: Use locator expectations for cleared logs to leverage auto‑wait

Relying on a one-time all() snapshot plus length can be racy if the UI updates slightly after the click. Using toHaveCount(0) on the locator gives you Playwright’s auto‑waiting:

-    const logsAfter = await page.getByText(/Successfully executed command/).all();
-    await expect(logsAfter.length).toBe(0);
+    const logsLocator = page.getByText(/Successfully executed command/);
+    await expect(logsLocator).toHaveCount(0);
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/pause-and-resume-log-streaming.spec.ts (1)

28-38: Tighten pause/resume and buffered-log assertions

This currently only re-checks the “Resume incoming data” button after executing the command, so it doesn’t really assert that logs were withheld while paused, and the final check uses a one-time locator('text=Successfully executed command').all(). Consider:

  • Asserting something about the log area before vs. after resume (e.g., comparing counts or checking absence of the new command log while paused).
  • Using a locator-based wait (e.g., await expect(page.getByText("Successfully executed command 'test'.")).toHaveCount(1) or a locator.count() + expect.poll) instead of all() + length to reduce flakiness.
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/example.spec.ts (1)

3-18: Isolate or skip external Playwright.dev sample in main test suite

These example tests hit https://playwright.dev/, which can introduce flaky failures unrelated to this project. You may want to mark them test.skip, move them under a docs/examples folder, or gate them with an env flag so normal CI runs only the Aspire-specific specs.

src/JavaScriptExtensions/GenerateTests/tests/UnitTests/start-resource-from-console-logs-page.spec.ts (1)

11-43: Prefer event-based waits (or shared helpers) over fixed sleeps

This test mixes setTimeout-style sleeps and page.waitForTimeout to wait for auth, page load, and logs, plus locator.all() + length checks. That makes it slower and more brittle than necessary. Consider:

  • Using Playwright’s built-in waits (page.waitForURL, expect(locator).toBeVisible(), expect(locator).toHaveCount(...)) for page/log readiness.
  • Or, reusing setupFirstPage / sleep from tests/common.ts so login and initial load logic is centralized and less timing-sensitive.
    Once you rely on locator expectations, most of the explicit sleeps can go away.
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/restart-command-disabled-state.spec.ts (1)

37-50: Avoid inferring disabled state from a failed click

The test treats a 2s click timeout/exception as proof that “Restart” is disabled, which can be brittle if the menu is just slow or blocked by some overlay. If the menuitem exposes disabled semantics (e.g., aria-disabled, a disabled attribute, or a specific CSS class), asserting that directly on restartLocator will be more robust than relying on an exception from .click().

src/JavaScriptExtensions/GenerateTests/tests/UnitTests/access-resource-actions-menu.spec.ts (1)

9-35: Reuse shared helpers instead of manual URLs and setTimeout sleeps

Here you hand-code the tokenized login URL, console logs URL, and two setTimeout-based waits. Since tests/common.ts already exposes things like BASE, LOGIN, sleep, and setupFirstPage, it would be cleaner and less flaky to:

  • Move auth/navigation into a helper, and
  • Replace the fixed 3s sleeps with locator-based expectations (e.g., expect(page.getByRole('button', { name: 'Resource actions' })).toBeVisible()).
    This also reduces duplication across the other console-logs specs.
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-start-command.spec.ts (1)

9-18: Consider extracting authentication and navigation to shared utilities.

The authentication and navigation flow is duplicated across multiple test files. The common.ts file appears to provide shared test infrastructure that could be leveraged.

Consider creating a helper function in common.ts:

export async function authenticateAndNavigate(page: Page, resourcePath: string) {
  await page.goto(`https://javascriptextensions.dev.localhost:17253/login?t=${process.env.AUTH_TOKEN}`);
  await page.waitForLoadState('networkidle');
  await page.goto(`https://javascriptextensions.dev.localhost:17253${resourcePath}`);
  await page.waitForLoadState('networkidle');
}

Then use it in tests:

await authenticateAndNavigate(page, '/consolelogs/resource/JavaScriptAppWithCommands');
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/expand-commands-submenu.spec.ts (1)

28-43: Inconsistent assertion patterns.

The test mixes two different assertion styles: .toBeVisible() (lines 28, 31, 43) and .all().length > 0 (lines 34-35, 39-40). Choose one approach and apply it consistently.

Prefer .toBeVisible() for simplicity:

-    const buildElements = await page.getByText('build').all();
-    expect(buildElements.length).toBeGreaterThan(0);
+    await expect(page.getByRole('menuitem', { name: 'build' })).toBeVisible();
     
-    const startElements = await page.getByText('start').all();
-    expect(startElements.length).toBeGreaterThan(0);
+    await expect(page.getByRole('menuitem', { name: 'start', exact: true })).toBeVisible();
src/JavaScriptExtensions/GenerateTests/test-plan-console-logs-commands.md (1)

15-19: Avoid hard-coding environment-specific URLs/tokens and full npm paths in expectations

The plan bakes in very specific details (dev host/port + token in login URLs, and full C:\\Program Files\\nodejs\\npm.cmd paths in expected logs). These are likely to drift from real environments and make both manual and automated checks brittle.

Consider:

  • Referring to URLs symbolically (e.g., “ASPIRE_LOGIN_URL”, “Console Logs URL for JavaScriptAppWithCommands”) or as examples, not fixed values.
  • Describing log expectations in terms of patterns (e.g., “contains npm run test in folder: {path}”) rather than a single hard-coded absolute path.

This keeps the plan valid across machines and CI environments while still being precise about behavior.

Also applies to: 28-31, 112-118, 151-153

src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)

36-61: Clarify video handling vs fixture teardown and tighten types

There are a couple of things here that are worth tightening up:

  1. Fixture vs endTest both closing the context
  • The page fixture creates a context and always calls await context.close() after use(page).
  • endTest also calls page.context().close() (without await) and then does video renaming.

If tests call endTest and also rely on the fixture, you’ll be closing the same context twice and racing that close with page.video().path() and fs.rename. At minimum this is redundant; in the worst case it can cause flaky timing around when the video file is actually finalized.

Consider choosing a single place to manage context lifetime + video handling (either the fixture teardown, or endTest, but not both) and making sure all tests follow that path.

  1. Minor type-safety improvements
  • extend<{ page: any }> and let context; both erase useful typing. You can keep things strongly typed with very small changes:
-import { Page,test as baseTest, expect, Locator, TestInfo } from '@playwright/test';
+import { Page, test as baseTest, expect, Locator, TestInfo, BrowserContext } from '@playwright/test';
@@
-export const test = baseTest.extend<{ page: any }>({
+export const test = baseTest.extend<{ page: Page }>({
   page: async ({ browser }, use, testInfo) => {
@@
-    let context;
+    let context: BrowserContext;

And, if you decide to keep endTest, it’s safer to at least await the close and avoid multiple page.video() calls:

-export async function endTest(page: Page, testInfo: TestInfo): Promise<void> {
-    if(page.video() !== null  ){
-        console.log(`Video saved to: ${await page.video()!.path()}`);
-        page.context().close();
-        await sleep(10);
-        var timeData = formatTimestamp();
-        await fs.rename( 
-          await page.video()!.path(),
-          path.join('videos', `video-${testInfo.title.replace(/[^a-zA-Z0-9-_]/g,'_')}-${timeData}.webm`));
-        
-    } 
-}
+export async function endTest(page: Page, testInfo: TestInfo): Promise<void> {
+  const video = page.video();
+  if (!video) return;
+
+  await page.context().close();
+  const videoPath = await video.path();
+  console.log(`Video saved to: ${videoPath}`);
+
+  const timeData = formatTimestamp();
+  await fs.rename(
+    videoPath,
+    path.join('videos', `video-${testInfo.title.replace(/[^a-zA-Z0-9-_]/g, '_')}-${timeData}.webm`),
+  );
+}

Please double-check this against how Playwright orders fixture teardown vs user code in your version to ensure there’s no deadlock around video.path() resolution.

Also applies to: 63-74

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43f2413 and 1449d93.

⛔ Files ignored due to path filters (48)
  • docs/images/JavaScriptExtensions/packageJson.mp4 is excluded by !**/*.mp4
  • src/JavaScriptExtensions/GenerateTests/package-lock.json is excluded by !**/package-lock.json
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/favicon.png is excluded by !**/*.png
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js is excluded by !**/dist/**, !**/*.min.js
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map is excluded by !**/dist/**, !**/*.map, !**/*.min.js.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js is excluded by !**/dist/**, !**/*.min.js
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map is excluded by !**/dist/**, !**/*.map, !**/*.min.js.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js is excluded by !**/dist/**
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map is excluded by !**/dist/**, !**/*.map
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js is excluded by !**/dist/**, !**/*.min.js
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map is excluded by !**/dist/**, !**/*.map, !**/*.min.js.map
  • src/JavaScriptExtensions/SampleJavascript/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (73)
  • src/AspireResourceExtensions/AspireResourceExtensionsAspire/AspireResourceExtensionsAspire.csproj (1 hunks)
  • src/AspireResourceExtensions/README.md (1 hunks)
  • src/DocumentorDatabaseExtensions/GenerateTest/tests/DemoTests/demoVideoDocumentor.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/.github/chatmodes/ 🎭 planner.chatmode.md (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/.github/chatmodes/🎭 generator.chatmode.md (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/.github/chatmodes/🎭 healer.chatmode.md (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/.gitignore (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/.vscode/mcp.json (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/package.json (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/playwright.config.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/test-plan-console-logs-commands.md (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/DemoTests/packageToCommands.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/access-resource-actions-menu.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/clear-console-log-data.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/example.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-build-command.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-dev-command.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-multiple-commands-sequentially.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-start-command.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-test-command.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/expand-commands-submenu.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/pause-and-resume-log-streaming.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/restart-command-disabled-state.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/seed.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/start-resource-from-console-logs-page.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/UnitTests/view-resource-details.spec.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/common.ts (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/config.json (1 hunks)
  • src/JavaScriptExtensions/GenerateTests/tests/showMessage.js (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.ApiService/JavaScriptExtensions.ApiService.csproj (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.ApiService/JavaScriptExtensions.ApiService.http (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.ApiService/Program.cs (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.ApiService/Properties/launchSettings.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.ApiService/appsettings.Development.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.ApiService/appsettings.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.AppHost/AppHost.cs (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.AppHost/JavaScriptExtensions.AppHost.csproj (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.AppHost/Properties/launchSettings.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.AppHost/appsettings.Development.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.AppHost/appsettings.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.ServiceDefaults/Extensions.cs (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.ServiceDefaults/JavaScriptExtensions.ServiceDefaults.csproj (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/App.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Layout/MainLayout.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Layout/MainLayout.razor.css (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Layout/NavMenu.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Layout/NavMenu.razor.css (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Pages/Counter.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Pages/Error.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Pages/Home.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Pages/Weather.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Routes.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/_Imports.razor (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/JavaScriptExtensions.Web.csproj (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Program.cs (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/Properties/launchSettings.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/WeatherApiClient.cs (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/appsettings.Development.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/appsettings.json (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.Web/wwwroot/app.css (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensions.slnx (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs (1 hunks)
  • src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptExtensionsAspire.csproj (1 hunks)
  • src/JavaScriptExtensions/LICENSE (1 hunks)
  • src/JavaScriptExtensions/README.md (1 hunks)
  • src/JavaScriptExtensions/SampleJavascript/package.json (1 hunks)
  • src/SqlServerExtensions/DataProject/DataProject.csproj (2 hunks)
  • src/SqlServerExtensions/SqlExtensionsAspire/SqlExtensionsAspire.csproj (2 hunks)
  • src/SqlServerExtensions/SqlServerExtensions.ApiService/SqlServerExtensions.ApiService.csproj (2 hunks)
  • src/SqlServerExtensions/SqlServerExtensions.AppHost/AppHost.cs (1 hunks)
  • src/SqlServerExtensions/SqlServerExtensions.AppHost/SqlServerExtensions.AppHost.csproj (1 hunks)
  • src/SqlServerExtensions/SqlServerExtensions.ServiceDefaults/SqlServerExtensions.ServiceDefaults.csproj (2 hunks)
  • src/SqlServerExtensions/SqlServerExtensions.Tests/SqlServerExtensions.Tests.csproj (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (18)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-test-command.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/start-resource-from-console-logs-page.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/view-resource-details.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/showMessage.js (1)
src/SqlServerExtensions/GenerateTest/tests/showMessage.js (1)
  • showMessageForVideo (1-36)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/clear-console-log-data.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/DemoTests/packageToCommands.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (6)
  • test (36-61)
  • setupFirstPage (80-93)
  • sleepMessage (106-116)
  • BASE (76-76)
  • sleep (102-104)
  • flashAndClick (118-140)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/access-resource-actions-menu.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/JavaScriptExtensions.ApiService/Program.cs (1)
src/BlazorExtensions/BlazorExtensions.ApiService/Program.cs (1)
  • WeatherForecast (62-65)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/seed.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs (1)
src/AspireResourceExtensions/AspireResourceExtensions.AppHost/JavaScriptAppExtension.cs (1)
  • JavaScriptAppExtension (9-155)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/example.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/restart-command-disabled-state.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/pause-and-resume-log-streaming.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-multiple-commands-sequentially.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/expand-commands-submenu.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-build-command.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-start-command.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-dev-command.spec.ts (1)
src/JavaScriptExtensions/GenerateTests/tests/common.ts (1)
  • test (36-61)
🪛 Biome (2.1.2)
src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Layout/MainLayout.razor.css

[error] 24-24: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 24-24: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 30-30: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 30-30: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 34-34: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 44-44: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 44-44: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 67-67: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)

src/JavaScriptExtensions/JavaScriptExtensions.Web/Components/Layout/NavMenu.razor.css

[error] 62-62: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 71-71: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)


[error] 76-76: Unexpected unknown pseudo-elements: deep

See MDN web docs for more details.
Use a known pseudo-elements instead, such as:

(lint/correctness/noUnknownPseudoElement)

🪛 LanguageTool
src/JavaScriptExtensions/GenerateTests/.github/chatmodes/🎭 healer.chatmode.md

[style] ~43-~43: Consider an alternative for the overused word “exactly”.
Context: ...at needs debugging and fixing, which is exactly what the healer agent is designed for. ...

(EXACTLY_PRECISELY)

src/JavaScriptExtensions/GenerateTests/.github/chatmodes/🎭 generator.chatmode.md

[style] ~57-~57: Consider an alternative for the overused word “exactly”.
Context: ...owser automation test created, which is exactly what the generator agent is designed fo...

(EXACTLY_PRECISELY)

🪛 markdownlint-cli2 (0.18.1)
src/JavaScriptExtensions/README.md

37-37: Bare URL used

(MD034, no-bare-urls)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CodeQL analysis (javascript-typescript)
  • GitHub Check: CodeQL analysis (csharp)
  • GitHub Check: build

Comment on lines +76 to +78
export const BASE = process.env.ASPIRE_BASE_URL ??" https://localhost:17261";
export const LOGIN = process.env.ASPIRE_LOGIN_URL??" https://localhost:17261/login?t=2dc508ebb9e7959d93fda0353ad5a987";
export const RESOURCE_URL = `${BASE}`;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove leading spaces from default URLs – current values are invalid

Both BASE and LOGIN default strings start with a space character:

export const BASE = process.env.ASPIRE_BASE_URL ??" https://localhost:17261";
export const LOGIN = process.env.ASPIRE_LOGIN_URL??" https://localhost:17261/login?t=2dc5...";

This yields URLs like " https://localhost:17261" which are invalid and will cause page.goto to fail.

-export const BASE = process.env.ASPIRE_BASE_URL ??" https://localhost:17261";
-export const LOGIN = process.env.ASPIRE_LOGIN_URL??" https://localhost:17261/login?t=2dc508ebb9e7959d93fda0353ad5a987"; 
+export const BASE = process.env.ASPIRE_BASE_URL ?? 'https://localhost:17261';
+export const LOGIN = process.env.ASPIRE_LOGIN_URL ?? 'https://localhost:17261/login?t=2dc508ebb9e7959d93fda0353ad5a987';
🤖 Prompt for AI Agents
In src/JavaScriptExtensions/GenerateTests/tests/common.ts around lines 76-78,
the default URL string literals for BASE and LOGIN include a leading space which
produces invalid URLs; remove the leading space characters from the default
strings (and ensure the template for RESOURCE_URL uses the corrected BASE), so
the fallbacks become valid URLs without leading whitespace.

Comment on lines +21 to +59
test('Demo Execute And See Package Commands', async ({ page },test) => {
await sleepMessage(page,`<h1>Starting demo commands from package.json </h1>
I will show how to execute the scripts from package.json as Aspire Commands :
<h1>builder.AddJavaScriptApp(...).AddNpmCommandsFromPackage()</h1>
<pre>
"scripts": {
"test": "echo \"Error: no test specified\" ",
"dev":"echo I am running dev",
"build":"echo I am building",
</pre>
<h1>"start":"echo I am starting"</h1>
<pre>
},
</pre>
<h1>See how "start" command will execute </h1>
`,7);

await page.goto(BASE+"consolelogs/resource/JavaScriptAppWithCommands");
await sleep(2);
// Locate the "Remove data" button in the toolbar (trash icon)
const removeButton = page.getByRole('button', { name: 'Remove data' });

// Click on "Remove data" button
await removeButton.click();
await sleep(2);
const removeAll = page.getByRole('menuitem', { name: 'Remove all' });
await removeAll.click();
await sleep(2);
await await flashAndClick(page.getByRole('button', { name: 'Resource actions' }));
await sleep(2);

// Hover over "Commands" to expand submenu
await page.getByRole('menuitem', { name: 'Commands' }).hover();
await sleep(2);

// Click on the "build" menuitem
await flashAndClick(page.getByRole('menuitem', { name: 'start',exact: true }));
await sleep(10);
await flashAndClick(page.getByText("I am starting").first());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Fix URL concatenation with BASE and remove redundant await

BASE is concatenated with "consolelogs/resource/..." without a leading slash, so if BASE does not end with / (the default in tests/common.ts currently doesn’t), this produces an invalid URL. There’s also a redundant await before flashAndClick. You can address both with:

-        await page.goto(BASE+"consolelogs/resource/JavaScriptAppWithCommands");
+        await page.goto(`${BASE}/consolelogs/resource/JavaScriptAppWithCommands`);
@@
-        await await flashAndClick(page.getByRole('button', { name: 'Resource actions' }));
+        await flashAndClick(page.getByRole('button', { name: 'Resource actions' }));

Double slashes in the middle of the URL are fine if BASE already includes a trailing /.



---

🏁 Script executed:

```shell
#!/bin/bash
# Find and display BASE constant definition
rg -n "BASE\s*=" tests/common.ts -A 2 -B 2

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Find flashAndClick function definition
rg -n "flashAndClick" -A 3 --type=ts | head -50

Length of output: 4592


🏁 Script executed:

#!/bin/bash
# Verify the full path structure and find common.ts
fd "common.ts" tests/

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Find BASE constant in the correct path
rg -n "BASE\s*=" src/JavaScriptExtensions/GenerateTests/tests/common.ts -A 2 -B 2

Length of output: 255


🏁 Script executed:

#!/bin/bash
# Also search for export BASE to find the definition
rg -n "export.*BASE" src/JavaScriptExtensions/GenerateTests/tests/common.ts -A 2 -B 2

Length of output: 343


🏁 Script executed:

#!/bin/bash
# Check the goto line in packageToCommands.spec.ts context
rg -n "page.goto" src/JavaScriptExtensions/GenerateTests/tests/DemoTests/packageToCommands.spec.ts -B 2 -A 2

Length of output: 205


Fix URL concatenation with BASE and remove redundant await

Both issues are confirmed:

  1. BASE defaults to "https://localhost:17261" (no trailing slash), so concatenating BASE+"consolelogs/..." produces an invalid URL with a missing slash between the port and path.
  2. flashAndClick is an async function, making the double await at line 49 redundant.
-        await page.goto(BASE+"consolelogs/resource/JavaScriptAppWithCommands");
+        await page.goto(`${BASE}/consolelogs/resource/JavaScriptAppWithCommands`);

-        await await flashAndClick(page.getByRole('button', { name: 'Resource actions' }));
+        await flashAndClick(page.getByRole('button', { name: 'Resource actions' }));
🤖 Prompt for AI Agents
In
src/JavaScriptExtensions/GenerateTests/tests/DemoTests/packageToCommands.spec.ts
around lines 21 to 59, fix two issues: ensure the URL is built with a slash
between BASE and the path (e.g., use
`${BASE}/consolelogs/resource/JavaScriptAppWithCommands` or otherwise append a
'/' when BASE has no trailing slash) so the request URL is valid, and remove the
redundant double await on the flashAndClick call (change `await await
flashAndClick(...)` to a single `await flashAndClick(...)`).

Comment on lines +1 to +37
function showMessageForVideo(message,seconds) {
//window.alert('test');
// Create a div for the message
const overlayDiv = document.createElement('div');
overlayDiv.style.position = 'fixed';
overlayDiv.style.top = '0';
overlayDiv.style.left = '0';
overlayDiv.style.width = '100%';
overlayDiv.style.height = '100%';
overlayDiv.style.backgroundColor = 'rgba(255, 204, 0, 0.8)'; // Semi-transparent yellow
overlayDiv.style.color = '#000';
overlayDiv.style.display = 'flex';
overlayDiv.style.alignItems = 'center';
overlayDiv.style.justifyContent = 'center';
overlayDiv.style.zIndex = '9999';
var table = document.createElement("table");
message.split('\n').forEach((line) => {
let tr = document.createElement('tr');
let td1 = document.createElement('td');
line=line.trim();
var newBlockDiv = document.createElement("div");
newBlockDiv.innerHTML=line;
td1.appendChild(newBlockDiv);
tr.appendChild(td1);
table.appendChild(tr);
});
overlayDiv.appendChild(table);
//overlayDiv.innerHTML = message;
// Append the div to the body
document.body.appendChild(overlayDiv);

// Set a timeout to remove or hide the message after 10 seconds
setTimeout(() => {
overlayDiv.remove(); // Or use messageDiv.style.display = 'none';
}, seconds*1000);
};
window.showMessageForVideo = showMessageForVideo; No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid innerHTML for arbitrary message text to reduce injection risk

Using newBlockDiv.innerHTML = line; means any <script> or HTML in message will be interpreted by the browser. If message ever contains app/user-provided data (even in tests), this can open you up to XSS-style issues.

If you don’t need rich HTML formatting here, prefer textContent:

-        var newBlockDiv = document.createElement("div");
-         newBlockDiv.innerHTML=line;
+        var newBlockDiv = document.createElement("div");
+        newBlockDiv.textContent = line;

If you do need HTML, consider sanitizing the string before assigning it.

🤖 Prompt for AI Agents
In src/JavaScriptExtensions/GenerateTests/tests/showMessage.js around lines
1-37, the code assigns user-controlled text into newBlockDiv.innerHTML which
allows HTML/script injection; replace the innerHTML assignment with a safe text
assignment (newBlockDiv.textContent = line) so the string is rendered as plain
text, or if rich HTML is actually required, run the message through a sanitizer
before assigning to innerHTML; keep the trimming and element-building logic and
ensure newline handling remains the same.

Comment on lines +4 to +45
import { test, expect } from '@playwright/test';

test.describe('Execute "build" Command', () => {
test('Scenario 4: Execute "build" Command', async ({ page }) => {
// Navigate to authentication URL to authenticate via token
await page.goto('https://javascriptextensions.dev.localhost:17253/login?t=99c3f542e0e4e1916be5148b2d2e1199');

// Wait for authentication to complete
await new Promise(f => setTimeout(f, 3 * 1000));

// Navigate to console logs page for JavaScriptAppWithCommands resource
await page.goto('https://javascriptextensions.dev.localhost:17253/consolelogs/resource/JavaScriptAppWithCommands');

// Wait for page to fully load
await new Promise(f => setTimeout(f, 3 * 1000));

// Open Resource actions menu
await page.getByRole('button', { name: 'Resource actions' }).click();

// Hover over "Commands" to expand submenu
await page.getByRole('menuitem', { name: 'Commands' }).hover();

// Click on the "build" menuitem
await page.getByRole('menuitem', { name: 'build' }).click();

// Verify "Executing command 'build'." log entry is visible
const executingElements = await page.getByText('Executing command \'build\'.').all();
expect(executingElements.length).toBeGreaterThan(0);

// Verify command execution path is logged
const execElements = await page.getByText('Exec C:\\Program Files\\nodejs\\npm.cmd run build in folder:').all();
expect(execElements.length).toBeGreaterThan(0);

//
// Verify build output "I am building" is visible
const buildingElements = await page.getByText('I am building').all();
expect(buildingElements.length).toBeGreaterThan(0);

// Verify "Successfully executed command 'build'." log entry is visible
const successMessage = await page.getByText('Successfully executed command \'build\'.').all();
expect(successMessage.length).toBeGreaterThan(0);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Improve test robustness (waiting), avoid hard-coded token, and consider OS‑agnostic log matching

A few points worth tightening up in this spec:

  1. Use Playwright’s waiting primitives instead of manual setTimeout

Hard sleeps make tests slower and flaky. Prefer page.waitForTimeout or, better, waiting on a specific condition (URL, element, or text):

-    // Wait for authentication to complete
-    await new Promise(f => setTimeout(f, 3 * 1000));
+    // Wait for authentication to complete
+    await page.waitForTimeout(3_000);
@@
-    // Wait for page to fully load
-    await new Promise(f => setTimeout(f, 3 * 1000));
+    // Wait for page to fully load
+    await page.waitForTimeout(3_000);

Even better would be something like await expect(page.getByText('Console logs')).toBeVisible(); tied to a stable UI element.

  1. Use expect on locators instead of .all() snapshots

locator.all() returns an immediate snapshot and does not auto‑retry; if the log line appears slightly later, the test fails. Using expect on a locator gives you Playwright’s built‑in retry:

-    const executingElements = await page.getByText('Executing command \'build\'.').all();
-    expect(executingElements.length).toBeGreaterThan(0);
+    await expect(page.getByText("Executing command 'build'.")).toBeVisible();
@@
-    const execElements = await page.getByText('Exec C:\\Program Files\\nodejs\\npm.cmd run build in folder:').all();
-    expect(execElements.length).toBeGreaterThan(0);
+    await expect(page.getByText('Exec')).toBeVisible(); // or a less OS-specific substring
@@
-    const buildingElements = await page.getByText('I am building').all();
-    expect(buildingElements.length).toBeGreaterThan(0);
+    await expect(page.getByText('I am building')).toBeVisible();
@@
-    const successMessage = await page.getByText('Successfully executed command \'build\'.').all();
-    expect(successMessage.length).toBeGreaterThan(0);
+    await expect(page.getByText("Successfully executed command 'build'.")).toBeVisible();

(Adjust the exact text or matchers as needed.)

  1. Avoid OS‑specific log text if you might run on non‑Windows agents

Matching the full Windows path (C:\\Program Files\\nodejs\\npm.cmd ...) will fail if the runner is Linux/macOS or if Node is installed elsewhere. If portability matters, assert on a more generic substring like 'npm run build' or a log message that’s independent of the absolute path.

  1. Be cautious with hard‑coded auth tokens

The login URL embeds a token in source control:

await page.goto('https://javascriptextensions.dev.localhost:17253/login?t=99c3f542e0e4e1916be5148b2d2e1199');

If this token is sensitive or reused elsewhere, consider moving it to configuration (env variable or config file) and constructing the URL at runtime, so secrets aren’t checked into the repo. If it’s a throwaway dev‑only token, then this is less of a concern, but worth double‑checking.

  1. Optional: Reuse the shared Playwright fixture

As with seed.spec.ts, if you want this test to benefit from the custom test fixture in tests/common.ts (video recording, showMessage.js init script, etc.), you could switch to:

-import { test, expect } from '@playwright/test';
+import { test, expect } from '../common';

(Again, adjust the path if needed.)


I'll verify the review comment by examining the codebase and confirming the identified issues.


---

🏁 Script executed:

```shell
#!/bin/bash
# Locate the test file and verify it exists
fd "execute-build-command.spec.ts" --type f

Length of output: 84


🏁 Script executed:

#!/bin/bash
# Check if tests/common.ts exists and examine its content
fd "common.ts" --path "*/tests/*" --type f

Length of output: 215


🏁 Script executed:

#!/bin/bash
# Look for seed.spec.ts to verify the fixture pattern mentioned in the review
fd "seed.spec.ts" --type f

Length of output: 262


🏁 Script executed:

#!/bin/bash
# Search for other files importing from common.ts to verify the pattern
rg "from.*common" --type ts --type tsx -n

Length of output: 27



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for common.ts in the JavaScriptExtensions directory structure
fd "common.ts" src/JavaScriptExtensions/GenerateTests/tests --type f

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Display the content of the test file to verify it matches the review snippet
cat src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-build-command.spec.ts

Length of output: 2027


🏁 Script executed:

#!/bin/bash
# Check the imports in the JavaScriptExtensions seed.spec.ts to see fixture pattern
head -20 src/JavaScriptExtensions/GenerateTests/tests/UnitTests/seed.spec.ts

Length of output: 159


🏁 Script executed:

#!/bin/bash
# Fix: Search for imports from common with correct syntax
rg "from.*common" --type ts -n

Length of output: 1059



---

🏁 Script executed:

```shell
#!/bin/bash
# Display the content of common.ts to see what fixtures and utilities it exports
cat src/JavaScriptExtensions/GenerateTests/tests/common.ts

Length of output: 4175


🏁 Script executed:

#!/bin/bash
# Search for test.extend or custom fixtures in common.ts files across the repo
rg "test\.extend" --type ts -A 5

Length of output: 0



---

🏁 Script executed:

```shell
#!/bin/bash
# Check other test files in the JavaScriptExtensions UnitTests directory for import patterns
rg "import.*from" src/JavaScriptExtensions/GenerateTests/tests/UnitTests --type ts -n | head -20

Length of output: 1930


🏁 Script executed:

#!/bin/bash
# Verify if execute-build-command.spec.ts is auto-generated by checking for seed references
rg "seed:" src/JavaScriptExtensions/GenerateTests/tests/UnitTests --type ts -n

Length of output: 1059


🏁 Script executed:

#!/bin/bash
# Verify Playwright's page.waitForTimeout exists and is the recommended approach
rg "waitForTimeout" --type ts -n

Length of output: 829


Improve test robustness: fix wait strategies, locator assertions, OS-specific text, and avoid hard-coded tokens

This test has several robustness issues worth addressing:

  1. Use page.waitForTimeout() instead of manual setTimeout

Hard sleeps make tests slower and flaky. Playwright's built-in timeout is cleaner:

-    // Wait for authentication to complete
-    await new Promise(f => setTimeout(f, 3 * 1000));
+    // Wait for authentication to complete
+    await page.waitForTimeout(3_000);
@@
-    // Wait for page to fully load
-    await new Promise(f => setTimeout(f, 3 * 1000));
+    // Wait for page to fully load
+    await page.waitForTimeout(3_000);
  1. Use expect on locators instead of .all() snapshots

locator.all() returns an immediate snapshot without auto-retry; if the log line appears slightly later, the test fails. Using expect gives you Playwright's built-in retry logic:

-    const executingElements = await page.getByText('Executing command \'build\'.').all();
-    expect(executingElements.length).toBeGreaterThan(0);
+    await expect(page.getByText("Executing command 'build'.")).toBeVisible();
@@
-    const execElements = await page.getByText('Exec C:\\Program Files\\nodejs\\npm.cmd run build in folder:').all();
-    expect(execElements.length).toBeGreaterThan(0);
+    await expect(page.getByText('Exec')).toBeVisible(); // or a less OS-specific substring
@@
-    const buildingElements = await page.getByText('I am building').all();
-    expect(buildingElements.length).toBeGreaterThan(0);
+    await expect(page.getByText('I am building')).toBeVisible();
@@
-    const successMessage = await page.getByText('Successfully executed command \'build\'.').all();
-    expect(successMessage.length).toBeGreaterThan(0);
+    await expect(page.getByText("Successfully executed command 'build'.")).toBeVisible();
  1. Avoid OS-specific log text

Matching the full Windows path (C:\\Program Files\\nodejs\\npm.cmd ...) will fail on Linux/macOS runners or if Node is installed elsewhere. Use a generic substring like 'npm run build' or a log pattern that's independent of the absolute path.

  1. Move hard-coded auth token to configuration

The login URL embeds a token in source control:

await page.goto('https://javascriptextensions.dev.localhost:17253/login?t=99c3f542e0e4e1916be5148b2d2e1199');

Consider moving it to an environment variable or config file (as done in common.ts with ASPIRE_LOGIN_URL) so sensitive tokens aren't checked into the repo.

🤖 Prompt for AI Agents
In
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-build-command.spec.ts
around lines 4–45, the test uses manual setTimeout sleeps, snapshot-style .all()
checks, hard-coded Windows-specific command path, and an in-repo auth token;
replace setTimeout calls with page.waitForTimeout(3000) or better
waitForLoadState where applicable, change locator checks from using .all() plus
length assertions to Playwright locator expectations with auto-retry (e.g.,
const locator = page.getByText(...); await expect(locator).toBeVisible({
timeout: ... }) or await expect(locator).toHaveCount(>0)), match the command
execution log with an OS-agnostic substring or pattern such as /npm run build/
or 'npm run build' instead of the full "C:\\Program Files\\nodejs\\npm.cmd ..."
path, and remove the hard-coded login token by using a configuration/env
variable (e.g., import or reference the existing common config like
ASPIRE_LOGIN_URL) so the auth URL is read from config rather than checked into
source control.

test.describe('Execute "dev" Command', () => {
test('Scenario 5: Execute "dev" Command', async ({ page }) => {
// Navigate to authentication URL to authenticate via token
await page.goto('https://javascriptextensions.dev.localhost:17253/login?t=99c3f542e0e4e1916be5148b2d2e1199');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Security: Remove hard-coded authentication token.

Same issue as in execute-start-command.spec.ts: the authentication token is hard-coded. Use environment variables instead.

🤖 Prompt for AI Agents
In
src/JavaScriptExtensions/GenerateTests/tests/UnitTests/execute-dev-command.spec.ts
around line 9, the test contains a hard-coded authentication token in the login
URL; replace the literal token with an environment variable (e.g.
process.env.TEST_AUTH_TOKEN) so the token is injected at runtime, construct the
URL using that variable (with proper encoding) and add a short guard that throws
or fails the test with a clear message if the env var is missing to avoid
silently passing with an empty token.

Comment on lines 35 to 39
See the how it looks in the

https://ignatandrei.github.io/aspireExtensions/images/JavaScriptExtensions/packageJson.mp4

![ShowUrl](https://ignatandrei.github.io/aspireExtensions/images/JavaScriptExtensions/packageJson.mp4)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdown formatting for video links.

The bare URL on line 37 and the image syntax on line 39 need correction:

  1. The bare URL violates markdown best practices (flagged by markdownlint)
  2. Using ![ShowUrl](...) for an MP4 file won't render properly in most markdown viewers

Based on static analysis.

-See the how it looks in the 
+See how it looks in the demo video:
 
-https://ignatandrei.github.io/aspireExtensions/images/JavaScriptExtensions/packageJson.mp4
-
-![ShowUrl](https://ignatandrei.github.io/aspireExtensions/images/JavaScriptExtensions/packageJson.mp4)
+[View Demo Video](https://ignatandrei.github.io/aspireExtensions/images/JavaScriptExtensions/packageJson.mp4)
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

37-37: Bare URL used

(MD034, no-bare-urls)

🤖 Prompt for AI Agents
In src/JavaScriptExtensions/README.md around lines 35 to 39, the README
currently contains a bare MP4 URL and uses image markdown for an .mp4 which
won't render; replace the bare URL with a proper markdown link (e.g., [Video:
packageJson demo](https://.../packageJson.mp4)) and remove the image-style
syntax for the MP4; to embed the video for viewers, either add an HTML5 <video>
tag pointing to the .mp4 (with controls and a fallback link) or create/point to
a thumbnail image and wrap that image in a markdown link to the .mp4 so viewers
can click the thumbnail to play the video.

"name": "samplejavascript",
"version": "1.0.0",
"description": "",
"main": "index.js",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify that the main entry point exists.

The package references "index.js" as the main entry point. Ensure this file exists in the same directory, or update the field if it should point elsewhere.


🏁 Script executed:

#!/bin/bash
# Description: Verify the main entry point file exists.

ls -la "$(dirname "$0")/index.js" 2>&1 || echo "index.js not found in $(dirname "$0")"

Length of output: 87


The previous script executed from the wrong directory. Let me verify the entry point file from the correct repository context.


---

🏁 Script executed:

```shell
#!/bin/bash
# Verify index.js exists in the SampleJavascript package directory
test -f "src/JavaScriptExtensions/SampleJavascript/index.js" && echo "✓ index.js exists" || echo "✗ index.js NOT found"
ls -la "src/JavaScriptExtensions/SampleJavascript/" 2>&1 | head -20

Length of output: 256


The main entry point file index.js does not exist.

Verification confirms that index.js is missing from src/JavaScriptExtensions/SampleJavascript/. Either create the file or remove/update the "main" field in package.json to point to a valid entry point.

🤖 Prompt for AI Agents
In src/JavaScriptExtensions/SampleJavascript/package.json around line 5,
package.json declares "main": "index.js" but that file does not exist; either
create a new index.js in src/JavaScriptExtensions/SampleJavascript implementing
the module's entry exports, or update/remove the "main" field to point to the
actual entry file (e.g., change to an existing file name or remove the field if
not needed) and ensure package consumers and any build/config scripts are
updated accordingly.

"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" ",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test script placeholder conflicts with PR objectives.

The PR aims to introduce Playwright test generation, but the test script currently echoes an error message. Either configure this to run actual tests (e.g., via Playwright) or add a TODO comment clarifying when this will be implemented.

-    "test": "echo \"Error: no test specified\" ",
+    "test": "playwright test",

If Playwright isn't set up yet, consider a more explicit note:

-    "test": "echo \"Error: no test specified\" ",
+    "test": "echo \"TODO: Configure Playwright test runner\"",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"test": "echo \"Error: no test specified\" ",
"test": "playwright test",
Suggested change
"test": "echo \"Error: no test specified\" ",
"test": "echo \"TODO: Configure Playwright test runner\"",
🤖 Prompt for AI Agents
In src/JavaScriptExtensions/SampleJavascript/package.json around line 7, the
"test" script is a placeholder that echoes an error and conflicts with the PR
goal of adding Playwright test generation; update the package.json so the "test"
script actually runs the Playwright test runner (configure it to invoke the
Playwright test command after Playwright is installed and configured) or, if
Playwright isn't ready yet, replace the placeholder with a clear TODO comment in
package.json explaining that Playwright tests will be wired into the "test"
script once the test harness is added.

;

var tests = builder.AddNpmApp("PlaywrightTests", "../GenerateTest")
var tests = builder.AddJavaScriptApp("PlaywrightTests", "../GenerateTest")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the API method change across the codebase.

The method call has been updated from AddNpmApp to AddJavaScriptApp. Ensure that:

  1. The AddJavaScriptApp method exists and has the expected signature
  2. All other usages of AddNpmApp in the codebase have been consistently updated

Run the following script to verify the method exists and check for any remaining AddNpmApp usages:


🏁 Script executed:

#!/bin/bash
# Description: Verify AddJavaScriptApp exists and check for AddNpmApp usages

# Search for the definition of AddJavaScriptApp
echo "=== Searching for AddJavaScriptApp definition ==="
rg -nP --type=cs -C3 'AddJavaScriptApp\s*\('

echo -e "\n=== Checking for any remaining AddNpmApp usages ==="
rg -nP --type=cs -C3 'AddNpmApp\s*\('

Length of output: 3382


Update DocumentorDatabaseExtensions AppHost to use AddJavaScriptApp for consistency.

The AddJavaScriptApp method exists and is correctly used in the file under review. However, verification found that AddNpmApp is still being used in src/DocumentorDatabaseExtensions/DocumentorDatabaseExtensions.AppHost/AppHost.cs:33, which should be updated to AddJavaScriptApp to maintain consistency with the migration pattern applied to other files.

🤖 Prompt for AI Agents
In src/SqlServerExtensions/SqlServerExtensions.AppHost/AppHost.cs around line 34
(the line using builder.AddJavaScriptApp("PlaywrightTests", "../GenerateTest")),
update
src/DocumentorDatabaseExtensions/DocumentorDatabaseExtensions.AppHost/AppHost.cs
at/around line 33 where AddNpmApp is used so it calls AddJavaScriptApp with the
same arguments instead; replace the AddNpmApp invocation with AddJavaScriptApp,
ensure the method parameters and relative path match the existing pattern, and
run a quick build to confirm no signature differences need addressing.

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify compatibility of major package version upgrades.

The file includes significant version bumps:

  • TargetFramework: net10.0 (major version)
  • Aspire.Hosting.JavaScript: 13.0.0 (replaces NodeJs package)
  • Aspire.Hosting.AppHost: 9.5.0 → 13.0.0 (major bump)
  • Microsoft.Extensions.Diagnostics.Testing: 9.9.0 → 10.0.0

Ensure these versions are compatible, available on NuGet, and do not introduce breaking changes. Also verify that net10.0 is consistently targeted across related projects in the codebase.

Also applies to: 15-15, 16-16, 24-24


🏁 Script executed:

#!/bin/bash
# Description: Verify package versions exist and check for related projects using older versions

# Check if packages exist on NuGet and retrieve version info
echo "=== Checking Aspire.Hosting.JavaScript v13.0.0 ==="
curl -s "https://api.nuget.org/v3-flatcontainer/aspire.hosting.javascript/13.0.0/aspire.hosting.javascript.nuspec" | head -20

echo -e "\n=== Checking Aspire.Hosting.AppHost v13.0.0 ==="
curl -s "https://api.nuget.org/v3-flatcontainer/aspire.hosting.apphost/13.0.0/aspire.hosting.apphost.nuspec" | head -20

echo -e "\n=== Checking for version inconsistencies in related projects ==="
fd -e csproj -e props | xargs grep -h "TargetFramework\|Aspire.Hosting.AppHost\|Aspire.AppHost.Sdk" | sort | uniq

Length of output: 3070


I need to verify .NET 10 compatibility with the Aspire packages being used, as the nuspec output shows they target net8.0.

I need to check if the version mixing in the codebase will cause problems and verify the compatibility information.

Now I need to check if the actual code in the repository uses the Aspire APIs that have breaking changes, and verify the scope of the version inconsistency:

[/run_scripts]


🏁 Script executed:

#!/bin/bash
# Search for usage of APIs that have breaking changes in Aspire 13.0
echo "=== Searching for AllocatedEndpoint usage ==="
rg -n "AllocatedEndpoint" --type=cs

echo -e "\n=== Searching for containerHostName usage ==="
rg -n "containerHostName" --type=cs

echo -e "\n=== Searching for EndpointReference/GetEndpoint usage ==="
rg -n "GetEndpoint|EndpointReference" --type=cs -A2 -B2

echo -e "\n=== Checking project files for Aspire package references ==="
fd -e csproj | xargs grep -l "Aspire" | head -10

echo -e "\n=== Listing all project files and their Aspire versions ==="
fd -e csproj -x sh -c 'echo "File: {}"; grep -E "TargetFramework|Aspire\." "{}" || true; echo ""'

Length of output: 16631


Update SDK version to match packages: change Aspire.AppHost.Sdk from 9.5.0 to 13.0.0.

File under review has a critical version mismatch: SDK version 9.5.0 paired with packages 13.0.0 (Aspire.Hosting.AppHost and Aspire.Hosting.JavaScript). The SDK version defines the Aspire framework runtime, and it must match the package versions. Correct this to use Aspire.AppHost.Sdk/13.0.0 to align with the package dependencies. This matches the pattern in other net10.0 projects (JavaScriptExtensions.AppHost and AspireResourceExtensions.AppHost), which consistently use 13.0.0 for both SDK and packages.

Packages exist on NuGet and are compatible with .NET 10. Code using older Aspire versions (9.x) remains on compatible versions with no breaking change issues.

🤖 Prompt for AI Agents
In
src/SqlServerExtensions/SqlServerExtensions.AppHost/SqlServerExtensions.AppHost.csproj
around line 7, the project SDK is set to Aspire.AppHost.Sdk 9.5.0 while package
dependencies target Aspire 13.0.0; update the SDK reference to
Aspire.AppHost.Sdk version 13.0.0 so SDK and package versions match the other
net10.0 AppHost projects (e.g., JavaScriptExtensions.AppHost), ensuring
compatibility with the Aspire.Hosting.AppHost and Aspire.Hosting.JavaScript
13.0.0 packages.

Refactored ExecuteProcess to use a using statement for process disposal and improved error handling logic. Updated LICENSE formatting and clarified README wording.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
src/JavaScriptExtensions/LICENSE (1)

3-3: Spacing issue resolved.

Good catch on the prior review—the copyright line now correctly includes proper spacing between (c) and the year.

src/JavaScriptExtensions/README.md (2)

15-15: Remove empty bullet point.

Line 15 contains an orphaned bullet point with no content, creating incorrect markdown list formatting.

Apply this diff:

 ## Requirements
 
 - .NET 10.0 or later
 - Aspire 13.0 or later
-
 ## Installation

35-37: Fix markdown formatting and incomplete text for video link.

The video link section has two issues:

  1. Line 35: The text is incomplete ("See how it looks in the" followed by a bare URL), and bare URLs violate markdown best practices (flagged by markdownlint as MD034).
  2. Line 37: Using image syntax ![...] for an MP4 file won't render properly in most markdown viewers.

Apply this diff to create a proper markdown link:

-See how it looks in the https://ignatandrei.github.io/aspireExtensions/images/JavaScriptExtensions/packageJson.mp4
+See how it looks in the demo video:
 
-![ShowUrl](https://ignatandrei.github.io/aspireExtensions/images/JavaScriptExtensions/packageJson.mp4)
+[View Demo Video](https://ignatandrei.github.io/aspireExtensions/images/JavaScriptExtensions/packageJson.mp4)
src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs (2)

31-31: Consider removing unused variable.

The commandValue variable is assigned but never read, as previously flagged.


10-18: Fix invalid extension method syntax to enable compilation.

This syntax error preventing compilation has already been flagged in previous reviews. The extension(...) construct is not valid C#.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1449d93 and 8530e25.

📒 Files selected for processing (3)
  • src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs (1 hunks)
  • src/JavaScriptExtensions/LICENSE (1 hunks)
  • src/JavaScriptExtensions/README.md (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
src/JavaScriptExtensions/README.md

35-35: Bare URL used

(MD034, no-bare-urls)

🔇 Additional comments (3)
src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs (3)

89-98: LGTM!

The FullExeName helper correctly searches the PATH environment variable for the executable and returns the full path or falls back to the command name.


100-109: LGTM!

The ConsumeOutput helper efficiently consumes process output asynchronously using a buffer and callback pattern.


161-179: LGTM!

The ExecuteProcessResult record provides a clean abstraction with computed properties for success detection and error message retrieval.

{
throw new FileNotFoundException($"Could not find package.json at {packageJsonPath}");
}
var packageJson = System.Text.Json.JsonDocument.Parse(File.ReadAllText(packageJsonPath));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Dispose of JsonDocument to prevent resource leak.

JsonDocument implements IDisposable and should be wrapped in a using statement to ensure proper cleanup.

Apply this diff:

-            var packageJson = System.Text.Json.JsonDocument.Parse(File.ReadAllText(packageJsonPath));
-            if (!packageJson.RootElement.TryGetProperty("scripts", out var scripts))
+            using var packageJson = System.Text.Json.JsonDocument.Parse(File.ReadAllText(packageJsonPath));
+            if (!packageJson.RootElement.TryGetProperty("scripts", out var scripts))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var packageJson = System.Text.Json.JsonDocument.Parse(File.ReadAllText(packageJsonPath));
using var packageJson = System.Text.Json.JsonDocument.Parse(File.ReadAllText(packageJsonPath));
if (!packageJson.RootElement.TryGetProperty("scripts", out var scripts))
🤖 Prompt for AI Agents
In src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs
around line 23, the JsonDocument returned by
JsonDocument.Parse(File.ReadAllText(packageJsonPath)) is not disposed; wrap the
call in a using (or using var) to ensure disposal — e.g., create the
JsonDocument inside a using block (or declare using var packageJson =
JsonDocument.Parse(...)) and move any subsequent code that reads from
packageJson inside that scope so the document is properly disposed when done.

Comment on lines +134 to +151
exportProcess.WaitForExit(timeout);

if (exportProcess.HasExited && exportProcess.ExitCode == 0 && string.IsNullOrWhiteSpace(resultError))
{
return ExecuteProcessResult.SuccessResult(resultStandard);
}
if (!exportProcess.HasExited)
{
exportProcess.Kill(true);
}
int nr = exportProcess.ExitCode;

if (nr != 0 || string.IsNullOrWhiteSpace(resultError))
{
resultError += "No error message provided. See previous messages";

}
return new ExecuteProcessResult(resultStandard, resultError, exportProcess.ExitCode);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix process timeout handling and prevent race condition with output consumption.

Multiple issues in the process execution logic:

  1. Line 134: WaitForExit(timeout) returns a boolean indicating whether the process exited within the timeout, but this return value is ignored.
  2. Line 136: Accessing exportProcess.ExitCode when the process may not have exited (if timeout occurred) can throw an InvalidOperationException.
  3. Lines 138, 151: Results are returned before the output consumption tasks (stdOutTask, stdErrTask) complete, potentially losing trailing output.

Apply this diff to fix these issues:

             var timeout = TimeSpan.FromMinutes(5);
-            exportProcess.WaitForExit(timeout);
+            bool exited = exportProcess.WaitForExit(timeout);
 
-            if (exportProcess.HasExited && exportProcess.ExitCode == 0 && string.IsNullOrWhiteSpace(resultError))
+            // Ensure output tasks complete before checking results
+            await Task.WhenAll(stdOutTask ?? Task.CompletedTask, stdErrTask ?? Task.CompletedTask);
+            
+            if (!exited)
             {
-                return ExecuteProcessResult.SuccessResult(resultStandard);
+                exportProcess.Kill(true);
+                return new ExecuteProcessResult(resultStandard, "Process timed out after 5 minutes", -1);
             }
-            if (!exportProcess.HasExited)
+            
+            if (exportProcess.ExitCode == 0 && string.IsNullOrWhiteSpace(resultError))
             {
-                exportProcess.Kill(true);
+                return ExecuteProcessResult.SuccessResult(resultStandard);
             }
-            int nr = exportProcess.ExitCode;
             
-            if (nr != 0 || string.IsNullOrWhiteSpace(resultError))
+            if (exportProcess.ExitCode != 0 && string.IsNullOrWhiteSpace(resultError))
             {
                 resultError += "No error message provided. See previous messages";
 
             } 
             return new ExecuteProcessResult(resultStandard, resultError, exportProcess.ExitCode);
         }
         finally
         {
-            await Task.WhenAll(stdOutTask ?? Task.CompletedTask, stdErrTask ?? Task.CompletedTask);
+            // Output tasks already awaited above
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportProcess.WaitForExit(timeout);
if (exportProcess.HasExited && exportProcess.ExitCode == 0 && string.IsNullOrWhiteSpace(resultError))
{
return ExecuteProcessResult.SuccessResult(resultStandard);
}
if (!exportProcess.HasExited)
{
exportProcess.Kill(true);
}
int nr = exportProcess.ExitCode;
if (nr != 0 || string.IsNullOrWhiteSpace(resultError))
{
resultError += "No error message provided. See previous messages";
}
return new ExecuteProcessResult(resultStandard, resultError, exportProcess.ExitCode);
bool exited = exportProcess.WaitForExit(timeout);
// Ensure output tasks complete before checking results
await Task.WhenAll(stdOutTask ?? Task.CompletedTask, stdErrTask ?? Task.CompletedTask);
if (!exited)
{
exportProcess.Kill(true);
return new ExecuteProcessResult(resultStandard, "Process timed out after 5 minutes", -1);
}
if (exportProcess.ExitCode == 0 && string.IsNullOrWhiteSpace(resultError))
{
return ExecuteProcessResult.SuccessResult(resultStandard);
}
if (exportProcess.ExitCode != 0 && string.IsNullOrWhiteSpace(resultError))
{
resultError += "No error message provided. See previous messages";
}
return new ExecuteProcessResult(resultStandard, resultError, exportProcess.ExitCode);
🤖 Prompt for AI Agents
In src/JavaScriptExtensions/JavaScriptExtensionsAspire/JavaScriptAppExtension.cs
around lines 134-151, fix process timeout and race conditions by capturing the
boolean result of exportProcess.WaitForExit(timeout) and using it to decide next
steps; if WaitForExit returns false, kill the process (exportProcess.Kill(true))
and call WaitForExit again to ensure it has exited before accessing ExitCode;
always await the stdout/stderr consumption tasks (stdOutTask and stdErrTask)
before returning so no output is lost; after ensuring the process has exited,
read exportProcess.ExitCode into a local variable and then construct the error
message only if the exit code indicates failure and no stderr was captured (or
append a default "No error message provided. See previous messages" when
appropriate); finally return the ExecuteProcessResult using the awaited output
and the safely-read exit code.

@ignatandrei ignatandrei merged commit 23196a8 into main Nov 22, 2025
1 of 2 checks passed
@ignatandrei ignatandrei deleted the Javascript branch November 22, 2025 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants