-
Notifications
You must be signed in to change notification settings - Fork 29
docs: update readme with current tech stack and github actions badge #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
38757d9
docs: update readme with current tech stack and github actions badge
chrisjwalk-bot 09fcb42
docs: make readme.md the source of truth, derive home.md from it
chrisjwalk-bot 3c85ece
dx: auto-sync home.md from readme.md via lint-staged
chrisjwalk-bot fb4f96b
docs: add features section and contributing note to readme
chrisjwalk-bot 978cc1c
ci: add coverage summary to job summary and pr comment
chrisjwalk-bot fe44722
docs: add husky/lint-staged and coverage to tooling section
chrisjwalk-bot 6745a11
test: add debug component tests and always run full test suite in ci
chrisjwalk-bot e157ccf
test: enforce per-file coverage thresholds, fix gaps, use affected te…
chrisjwalk-bot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| module.exports = { | ||
| '*.{ts,js}': 'eslint --cache --cache-location=.husky/_ --fix', | ||
| '*.{ts,js,css,scss,md,mdx}': 'prettier --write', | ||
| 'README.md': () => [ | ||
| 'pnpm nx run web-app:update-readme', | ||
| 'git add apps/web-app/src/assets/home.md', | ||
| ], | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,78 +1,98 @@ | ||
| [](https://freshpondmedia.visualstudio.com/FreshPondMediaGit/_build/latest?definitionId=43) | ||
| [](https://github.com/chrisjwalk/angular-cli-netcore-ngrx-starter/actions/workflows/ci.yml) | ||
|
|
||
| # Nx + Angular + .NET 10.0 | ||
|
|
||
| This is basic demo of how to use a full stack [Nx monorepo](https://nx.dev/getting-started/tutorials/angular-monorepo-tutorial) with [Angular](https://angular.dev) and .NET 10.0 with [Microsoft.AspNetCore.SpaServices.Extensions](https://docs.microsoft.com/en-us/aspnet/core/client-side/spa/angular) and a demo Azure pipeline for Azure DevOps. | ||
| A full-stack demo using an [Nx monorepo](https://nx.dev) with [Angular](https://angular.dev) (zoneless, signals) and a .NET 10.0 Web API backend. Deployed to Azure App Service with automated PR preview deployments via Azure Static Web Apps. | ||
|
|
||
| ## Demo | ||
| ## Features | ||
|
|
||
| See a live demo here: [https://angularclinetcorengrxstarter.azurewebsites.net/](https://angularclinetcorengrxstarter.azurewebsites.net/) | ||
| - **Authentication** — register, login, and logout with JWT bearer tokens backed by ASP.NET Core Identity | ||
| - **Notification centre** — persistent notification panel with unread count, mark-as-read, dismiss, and action support (e.g. one-click reload on SW update) | ||
| - **PWA / service worker** — offline support; notifies users when a new app version is available with an in-app prompt to reload | ||
| - **Debug page** (`/debug`) — trigger test notifications and inspect service worker update state during development | ||
| - **PR preview deployments** — every pull request gets a live preview URL via Azure Static Web Apps | ||
|
|
||
| ## Getting Started? | ||
| ## Tech stack | ||
|
|
||
| - **Make sure you have at least Node 24.x or higher (w/ pnpm 10+) installed!** | ||
| - **This repository uses ASP.NET 10.0, which has a hard requirement on .NET SDK 10.0.x. Please install these items from [https://dotnet.microsoft.com/download](https://dotnet.microsoft.com/download)** | ||
| **Frontend** | ||
|
|
||
| ## Visual Studio 2022 | ||
| - [Angular 21](https://angular.dev) — zoneless change detection, standalone components, signals | ||
| - [NgRx Signal Store](https://ngrx.io/guide/signals) — reactive state management | ||
| - [Angular Material](https://material.angular.io) — UI component library | ||
| - [Tailwind CSS v4](https://tailwindcss.com) — utility-first styling | ||
| - [Angular PWA](https://angular.dev/ecosystem/service-workers) — service worker & offline support | ||
|
|
||
| Make sure you have .NET 10.0 installed and/or the latest VS2026. | ||
| **Backend** | ||
|
|
||
| ## Visual Studio Code | ||
| - [.NET 10.0](https://dotnet.microsoft.com) Web API | ||
| - [ASP.NET Core Identity](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity) — bearer token authentication | ||
| - [Entity Framework Core](https://learn.microsoft.com/en-us/ef/core/) with Azure SQL | ||
|
|
||
| > Note: Make sure you have the C# extension & .NET Debugger installed. | ||
| **Tooling** | ||
|
|
||
| pnpm install | ||
| - [Nx](https://nx.dev) — monorepo build system with affected commands | ||
| - [Vitest](https://vitest.dev) — unit tests with ~93% line coverage | ||
| - [Playwright](https://playwright.dev) — end-to-end tests | ||
| - [Husky](https://typicode.github.io/husky/) + [lint-staged](https://github.com/lint-staged/lint-staged) — pre-commit hooks for linting, formatting, and keeping `home.md` in sync | ||
| - [pnpm](https://pnpm.io) — package manager | ||
|
|
||
| ## Serve Development App | ||
| ## Demo | ||
|
|
||
| ``` | ||
| pnpm start | ||
| ``` | ||
| Live demo: [https://angularclinetcorengrxstarter.azurewebsites.net/](https://angularclinetcorengrxstarter.azurewebsites.net/) | ||
|
|
||
| ## Getting started | ||
|
|
||
| Both the api (dotnet) and web app (Angular) will build and run in dev mode. Open your browser on http://localhost:4200/ to see the Angular app, or https://localhost:60254/swagger to see the api documentation generated by Swagger. | ||
| **Prerequisites** | ||
|
|
||
| ## Serve Production App (PWA enabled) | ||
| - Node 24.x+ with pnpm 10+ | ||
| - .NET SDK 10.0.x — [download](https://dotnet.microsoft.com/download) | ||
|
|
||
| **Install dependencies** | ||
|
|
||
| ```bash | ||
| pnpm install | ||
| ``` | ||
| pnpm serve:prod | ||
|
|
||
| ## Serve development app | ||
|
|
||
| ```bash | ||
| pnpm start | ||
| ``` | ||
|
|
||
| Starts both the .NET API and Angular app in dev mode. Open [http://localhost:4200](http://localhost:4200) for the app, or [https://localhost:60254/swagger](https://localhost:60254/swagger) for the API docs. | ||
|
|
||
| ## Lint | ||
|
|
||
| ``` | ||
| ```bash | ||
| pnpm lint | ||
| ``` | ||
|
|
||
| ## Unit Tests | ||
|
|
||
| Run unit tests by executing: | ||
| ## Unit tests | ||
|
|
||
| ``` | ||
| ```bash | ||
| pnpm test | ||
| ``` | ||
|
|
||
| Please note that for test coverage you need dotnet-coverage to be installed: | ||
| Coverage requires `dotnet-coverage`: | ||
|
|
||
| ``` | ||
| ```bash | ||
| dotnet tool install --global dotnet-coverage | ||
| ``` | ||
|
|
||
| More information is available on https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test | ||
|
|
||
| ## End-to-end Tests | ||
|
|
||
| Run e2e tests by executing: | ||
| ## End-to-end tests | ||
|
|
||
| ``` | ||
| ```bash | ||
| pnpm e2e | ||
| ``` | ||
|
|
||
| ## Build Production App | ||
|
|
||
| Build the production Angular app and Publish the release .NET app, run: | ||
| ## Build for production | ||
|
|
||
| ``` | ||
| ```bash | ||
| pnpm build:prod | ||
| ``` | ||
|
|
||
| The contents of the. `/dist` folder should now contain something that can be deployed to and Azure web service or IIS instance. | ||
| Builds the Angular app and publishes the .NET project to `/dist`, ready to deploy to Azure App Service. | ||
|
|
||
| ## Contributing | ||
|
|
||
| `apps/web-app/src/assets/home.md` is auto-generated from this file — **edit `README.md` only**. The lint-staged hook regenerates `home.md` automatically whenever `README.md` is committed. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| import { provideNoopAnimations } from '@angular/platform-browser/animations'; | ||
| import { NotificationStore } from '@myorg/shared'; | ||
| import { fireEvent, render, screen } from '@testing-library/angular'; | ||
|
|
||
| import { Debug } from './debug'; | ||
|
|
||
| async function setup() { | ||
| const { fixture } = await render(Debug, { | ||
| providers: [provideNoopAnimations(), NotificationStore], | ||
| }); | ||
| const store = fixture.componentInstance.store; | ||
| return { store }; | ||
| } | ||
|
|
||
| describe('Debug', () => { | ||
| it('should render debug tools heading', async () => { | ||
| await setup(); | ||
| expect(screen.getByText(/debug tools/i)).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('addInfo should add an info notification', async () => { | ||
| const { store } = await setup(); | ||
| fireEvent.click(screen.getByText('Add info')); | ||
| expect(store.notifications().length).toBe(1); | ||
| expect(store.notifications()[0].kind).toBe('info'); | ||
| }); | ||
|
|
||
| it('addError should add an error notification', async () => { | ||
| const { store } = await setup(); | ||
| fireEvent.click(screen.getByText('Add error')); | ||
| expect(store.notifications()[0].kind).toBe('error'); | ||
| }); | ||
|
|
||
| it('addAuth should add an auth notification', async () => { | ||
| const { store } = await setup(); | ||
| fireEvent.click(screen.getByText('Add auth')); | ||
| expect(store.notifications()[0].kind).toBe('auth'); | ||
| }); | ||
|
|
||
| it('addSwUpdate should add a sw-update notification with reload action', async () => { | ||
| const { store } = await setup(); | ||
| fireEvent.click(screen.getByText('Add sw-update')); | ||
| const notification = store.notifications()[0]; | ||
| expect(notification.kind).toBe('sw-update'); | ||
| expect(notification.action?.label).toBe('Reload'); | ||
| }); | ||
|
|
||
| it('addWithAction should add a notification with an action', async () => { | ||
| const { store } = await setup(); | ||
| fireEvent.click(screen.getByText('Add with action')); | ||
| expect(store.notifications()[0].action?.label).toBe('Do it'); | ||
| }); | ||
|
|
||
| it('addAutoDismiss should add a notification with autoDismissMs', async () => { | ||
| const { store } = await setup(); | ||
| fireEvent.click(screen.getByText('Add auto-dismiss (3s)')); | ||
| expect(store.notifications()[0].autoDismissMs).toBe(3000); | ||
| }); | ||
|
|
||
| it('clearAll should dismiss all notifications', async () => { | ||
| const { store } = await setup(); | ||
| fireEvent.click(screen.getByText('Add info')); | ||
| fireEvent.click(screen.getByText('Add error')); | ||
| expect(store.notifications().length).toBe(2); | ||
| fireEvent.click(screen.getByText('Clear all')); | ||
| expect(store.notifications().length).toBe(0); | ||
| }); | ||
|
|
||
| it('mark all read should mark all notifications as read', async () => { | ||
| const { store } = await setup(); | ||
| fireEvent.click(screen.getByText('Add info')); | ||
| fireEvent.click(screen.getByText('Add error')); | ||
| expect(store.unreadCount()).toBe(2); | ||
| fireEvent.click(screen.getByText('Mark all read')); | ||
| expect(store.unreadCount()).toBe(0); | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.