I'm at the "need to write more tests" phase, and there's still one more suppressor to write, but hopefully I can finish things in this stream if my brain will allow it
https://github.com/JasonBock/Rocks/issues/396
#dotnet #csharp #mocking
I'm at the "need to write more tests" phase, and there's still one more suppressor to write, but hopefully I can finish things in this stream if my brain will allow it
https://github.com/JasonBock/Rocks/issues/396
#dotnet #csharp #mocking
When working with CoPilot in Visual Studio, the single most important thing is context. Context tells CoPilot what it is working on, what it should already know, what language conventions to use, etc., etc. CoPilot wants to help, but its memory is not the best, and like the character in Memento, the trick is to write everything down and remind it all the time… hence context.
The best way to get ahead of this is to create a copilot-instructions.md file in your .github directory (which you may need to create). In this file, you’ll want to put anything that CoPilot should know right from the get-go and for every session.
But how do you know what to put in there?
Here’s the trick… let CoPilot build it for you! I cribbed a set of instructions from the ‘net that tells CoPilot how to create the file based on the existing code in my project. I tweaked it just a bit, as did a buddy (Teja) and all you need to do is copy it into the CoPilot window in Visual Studio, let CoPilot cook, and hey! presto! you have just what you want: a fully developed copilot-instructions.md file in just the right place. Even better, if you already have one, this will merge cleanly.
Here’s the instructions I give CoPilot:
Analyze this codebase to generate or update .github/copilot-instructions.md for guiding AI coding agents. Focus on discovering the essential knowledge that would help an AI agent be immediately productive in this codebase. Consider aspects like: The “big picture” architecture that requires reading multiple files to understand – major components, service boundaries, data flows, and the “why” behind structural decisions Critical developer workflows (builds, tests, debugging) especially commands that aren’t obvious from file inspection alone Project-specific conventions and patterns that differ from common practices Integration points, external dependencies, and cross-component communication patterns Guidelines (read more at https://aka.ms/vscode-instructions-docs):
If .github/copilot-instructions.md exists, merge intelligently – preserve valuable content while updating outdated sections Write concise, actionable instructions (~20-50 lines) using markdown structure Include specific examples from the codebase when describing patterns Avoid generic advice (“write tests”, “handle errors”) – focus on THIS project’s specific approaches Document only discoverable patterns, not aspirational practices Maintaining a history or features listing with a detailed description of all the information of the project into a README.md file whenever there is a new change. Update the file accordingly with a brief summary. Before each task implementation, go through the README.md file, then start implementing, understanding the whole picture of the requirements. Reference key files/directories that exemplify important patterns Update .github/copilot-instructions.md for the user, then ask for feedback on any unclear or incomplete sections to iterate.When writing unit tests use xunit and moq
And here is the markdown file that CoPilot generated for me (put this into a file and open it in VS to see it displayed nicely):
# GitHub Copilot Instructions for HyperVideo
HyperVideo is an interactive video learning platform built with **.NET 10** and **Blazor Server**. Users can pause videos, ask questions, and search multiple knowledge sources for learning resources.
## Project Structure
```
HyperVideo/
├── Components/
│ ├── App.razor # Root component (HTML shell, Bootstrap, Blazor script)
│ ├── Routes.razor # Router with 404 fallback and default layout
│ ├── _Imports.razor # Global using directives for all components
│ ├── Layout/
│ │ ├── MainLayout.razor # Two-column layout: sidebar NavMenu + main content area
│ │ ├── NavMenu.razor # Navigation links (Home, Video Learning, Counter, Weather)
│ │ └── ReconnectModal.razor # SignalR reconnection overlay
│ └── Pages/
│ ├── Home.razor # Landing page with link to video learning
│ ├── VideoLearning.razor # Core feature: interactive video player with inquiry panel
│ ├── Counter.razor # Simple stateful counter demo
│ ├── Weather.razor # Async data fetch demo with streaming render
│ ├── Error.razor # Error boundary page
│ └── NotFound.razor # 404 page
├── wwwroot/
│ ├── app.css # Global styles
│ └── lib/ # Bootstrap 5 CSS/JS
├── Program.cs # App startup and middleware pipeline
├── appsettings.json
└── HyperVideo.csproj
```
## Technology Stack
- **Runtime**: .NET 10 (`net10.0`)
- **UI**: Blazor Server with Interactive Server rendering (`@rendermode InteractiveServer`)
- **Real-time**: ASP.NET Core SignalR (used implicitly by Blazor Server)
- **CSS**: Bootstrap 5, Bootstrap Icons (`bi-*`), component-scoped CSS files (`.razor.css`)
- **JS Interop**: `IJSRuntime` for controlling the HTML5 `<video>` element
- **Nullable**: enabled; **ImplicitUsings**: enabled
## Coding Conventions
### Blazor Components
- Place all pages under `Components/Pages/` with `@page "/route"` directives.
- Place shared layout under `Components/Layout/`.
- Use `@rendermode InteractiveServer` on pages that need interactivity (event handlers, JS interop).
- Component-scoped CSS goes in a sibling file named `ComponentName.razor.css`.
- Use `@code { }` blocks at the bottom of `.razor` files for C# logic.
### C# Style
- Follow standard C# conventions: PascalCase for types and methods, camelCase for private fields.
- Prefer `record` types for immutable data (e.g., `TranscriptSegment`).
- Use nullable reference types; annotate `string?` where null is expected.
- Use `string.Empty` instead of `""` for empty-string defaults.
- Prefer `async Task` over `async void` for event handlers.
### JavaScript Interop
- Inject `IJSRuntime` with `@inject IJSRuntime JS`.
- Prefer `JS.InvokeVoidAsync` for fire-and-forget calls and `JS.InvokeAsync<T>` when a return value is needed.
- Avoid `eval()` for new interop code; instead, expose named JavaScript functions in `wwwroot/` and call them by name.
- Always wrap JS interop calls in `try/catch` because the circuit may not be ready.
### State Management
- Use private fields and `StateHasChanged()` for local component state.
- Keep models close to where they are used; only extract to a separate file when shared across multiple components.
### Error Handling
- Wrap async event handlers in `try/catch` and log errors with `Console.WriteLine` or an injected `ILogger<T>`.
- Use the `Error.razor` page as the global error boundary (already wired via `<ErrorBoundary>`).
## Key Feature: VideoLearning.razor
`VideoLearning.razor` is the core component. Its responsibilities:
| Concern | Implementation |
|---|---|
| Video playback | HTML5 `<video>` element; JS interop to call `.pause()`, `.play()`, seek |
| Pause & Inquire | Sets `isPaused = true`, shows inquiry panel with timestamp |
| Question search | Builds URL links to Wikipedia, Wiktionary, YouTube, Stack Overflow, MDN |
| Transcript | `List<TranscriptSegment>` displayed as clickable rows; active row highlighted |
| Time tracking | `@ontimeupdate` reads `currentTime` via JS interop |
When extending this component:
- Add new search sources by appending to `searchResults` inside `SearchForAnswer()`.
- Add transcript segments by adding `new TranscriptSegment(start, end, "text")` entries.
- Use `ElementReference` + `JS.InvokeVoidAsync("blazorHelpers.seekVideo", videoElement, time)` style helpers rather than bare `eval()`.
## Common Patterns
### Adding a New Page
```razor
@page "/my-page"
@rendermode InteractiveServer
<PageTitle>My Page</PageTitle>
<!-- markup -->
@code {
// component logic
}
```
Then add a `<NavLink>` entry in `NavMenu.razor`.
### Injecting a Service
Register in `Program.cs`:
```csharp
builder.Services.AddScoped<IMyService, MyService>();
```
Inject in component:
```razor
@inject IMyService MyService
```
### JS Interop Helper Pattern
Add a function to `wwwroot/app.js` (create if it does not exist):
```javascript
window.blazorHelpers = {
seekVideo: (element, time) => { element.currentTime = time; }
};
```
Call from C#:
```csharp
await JS.InvokeVoidAsync("blazorHelpers.seekVideo", videoElement, time);
```
## Build & Run
```bash
# Restore and run
dotnet run --project HyperVideo/HyperVideo.csproj
# Build only
dotnet build HyperVideo/HyperVideo.csproj
```
Default URLs: `http://localhost:5092` / `https://localhost:7288`
## What to Avoid
- Do **not** use `@rendermode InteractiveWebAssembly` — this project is Blazor Server only.
- Do **not** add NuGet packages without checking compatibility with `net10.0`.
- Do **not** use bare `eval()` strings for new JS interop; prefer named helper functions.
- Do **not** use `async void` event handlers; use `async Task` and handle exceptions.
- Do **not** store secrets in `appsettings.json`; use environment variables or the .NET Secret Manager.
This is based on the skeleton program written in the past couple of postings, so it didn’t have all that much to go on. Nonetheless, it created a great context file with very specific instructions for CoPilot that will create a consistent interface and programming approach.
More to come as we develop HyperVideo but this will put us on the right track straight away.



Learn when to use Builder pattern in C# with clear decision criteria, code examples, and scenarios. Understand the signs that indicate Builder is the right choice.