Karma Testing Skill Core Patterns karma.conf.js Test with Jasmine Framework Angular Integration Setup: Run: or Init: Deep Patterns See for production-grade patterns: | Section | What You Get | |---------|-------------| | §1 Production Configuration | Full karma.conf.js with coverage thresholds, reporters, CI launchers | | §2 Component Testing | Service mocking, DOM interaction, form validation patterns | | §3 HTTP Service Testing | HttpTestingController, error handling, retry testing | | §4 Directive & Pipe Testing | Host component pattern, custom pipes with edge cases | | §5 RxJS & Async Pat…

, '')));\n\n expect(prices).toEqual([4.99, 9.99, 19.99]);\n });\n\n it('should confirm before deleting', fakeAsync(() => {\n spyOn(window, 'confirm').and.returnValue(true);\n productService.delete.and.returnValue(of(void 0));\n\n const deleteBtn = fixture.nativeElement.querySelector('[data-testid=\"delete-btn-1\"]');\n deleteBtn.click();\n tick();\n\n expect(window.confirm).toHaveBeenCalled();\n expect(productService.delete).toHaveBeenCalledWith(1);\n }));\n});\n```\n\n---\n\n## §3 Service Testing with HTTP\n\n### HttpClient Testing\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';\nimport { UserService } from './user.service';\n\ndescribe('UserService', () => {\n let service: UserService;\n let httpMock: HttpTestingController;\n\n beforeEach(() => {\n TestBed.configureTestingModule({\n imports: [HttpClientTestingModule],\n providers: [UserService],\n });\n service = TestBed.inject(UserService);\n httpMock = TestBed.inject(HttpTestingController);\n });\n\n afterEach(() => {\n httpMock.verify(); // Ensure no outstanding requests\n });\n\n it('should fetch users with pagination', () => {\n const mockResponse = {\n data: [{ id: 1, name: 'Alice' }],\n total: 50,\n page: 1,\n };\n\n service.getUsers(1, 10).subscribe((response) => {\n expect(response.data.length).toBe(1);\n expect(response.total).toBe(50);\n });\n\n const req = httpMock.expectOne('/api/users?page=1&limit=10');\n expect(req.request.method).toBe('GET');\n expect(req.request.headers.get('Accept')).toBe('application/json');\n req.flush(mockResponse);\n });\n\n it('should handle 404 errors', () => {\n service.getUser(999).subscribe({\n error: (err) => {\n expect(err.status).toBe(404);\n expect(err.statusText).toBe('Not Found');\n },\n });\n\n const req = httpMock.expectOne('/api/users/999');\n req.flush('Not found', { status: 404, statusText: 'Not Found' });\n });\n\n it('should retry failed requests', () => {\n service.getUsersWithRetry().subscribe((users) => {\n expect(users.length).toBe(1);\n });\n\n // First attempt fails\n const req1 = httpMock.expectOne('/api/users');\n req1.error(new ProgressEvent('Network error'));\n\n // Retry succeeds\n const req2 = httpMock.expectOne('/api/users');\n req2.flush([{ id: 1, name: 'Alice' }]);\n });\n\n it('should send POST with correct body', () => {\n const newUser = { name: 'Bob', email: '[email protected]' };\n\n service.createUser(newUser).subscribe((user) => {\n expect(user.id).toBeDefined();\n });\n\n const req = httpMock.expectOne('/api/users');\n expect(req.request.method).toBe('POST');\n expect(req.request.body).toEqual(newUser);\n req.flush({ id: 2, ...newUser });\n });\n});\n```\n\n---\n\n## §4 Directive & Pipe Testing\n\n### Custom Directive\n```typescript\nimport { Component } from '@angular/core';\nimport { ComponentFixture, TestBed } from '@angular/core/testing';\nimport { HighlightDirective } from './highlight.directive';\n\n@Component({\n template: `\n \u003cp appHighlight=\"yellow\" data-testid=\"highlighted\">Highlighted text\u003c/p>\n \u003cp data-testid=\"plain\">Plain text\u003c/p>\n `,\n})\nclass TestHostComponent {}\n\ndescribe('HighlightDirective', () => {\n let fixture: ComponentFixture\u003cTestHostComponent>;\n\n beforeEach(() => {\n TestBed.configureTestingModule({\n declarations: [TestHostComponent, HighlightDirective],\n });\n fixture = TestBed.createComponent(TestHostComponent);\n fixture.detectChanges();\n });\n\n it('should apply background color', () => {\n const el = fixture.nativeElement.querySelector('[data-testid=\"highlighted\"]');\n expect(el.style.backgroundColor).toBe('yellow');\n });\n\n it('should change color on hover', () => {\n const el = fixture.nativeElement.querySelector('[data-testid=\"highlighted\"]');\n el.dispatchEvent(new Event('mouseenter'));\n fixture.detectChanges();\n expect(el.style.backgroundColor).toBe('gold');\n });\n});\n```\n\n### Custom Pipe\n```typescript\nimport { TruncatePipe } from './truncate.pipe';\n\ndescribe('TruncatePipe', () => {\n const pipe = new TruncatePipe();\n\n it('should truncate long strings', () => {\n expect(pipe.transform('Hello World!', 5)).toBe('Hello...');\n });\n\n it('should return short strings unchanged', () => {\n expect(pipe.transform('Hi', 10)).toBe('Hi');\n });\n\n it('should handle null/undefined', () => {\n expect(pipe.transform(null as any)).toBe('');\n expect(pipe.transform(undefined as any)).toBe('');\n });\n\n it('should use custom suffix', () => {\n expect(pipe.transform('Hello World!', 5, ' [more]')).toBe('Hello [more]');\n });\n});\n```\n\n---\n\n## §5 RxJS & Async Patterns\n\n### Testing Observables\n```typescript\nimport { fakeAsync, tick, flush, discardPeriodicTimers } from '@angular/core/testing';\nimport { of, interval, Subject, timer } from 'rxjs';\nimport { debounceTime, switchMap, take, delay } from 'rxjs/operators';\n\ndescribe('SearchComponent', () => {\n let component: SearchComponent;\n let fixture: ComponentFixture\u003cSearchComponent>;\n let searchService: jasmine.SpyObj\u003cSearchService>;\n\n beforeEach(async () => {\n searchService = jasmine.createSpyObj('SearchService', ['search']);\n await TestBed.configureTestingModule({\n declarations: [SearchComponent],\n providers: [{ provide: SearchService, useValue: searchService }],\n }).compileComponents();\n\n fixture = TestBed.createComponent(SearchComponent);\n component = fixture.componentInstance;\n fixture.detectChanges();\n });\n\n it('should debounce search input', fakeAsync(() => {\n searchService.search.and.returnValue(of([{ id: 1, name: 'Result' }]));\n\n component.searchControl.setValue('hel');\n tick(100);\n component.searchControl.setValue('hello');\n tick(300); // debounceTime(300)\n\n expect(searchService.search).toHaveBeenCalledTimes(1);\n expect(searchService.search).toHaveBeenCalledWith('hello');\n\n discardPeriodicTimers();\n }));\n\n it('should cancel previous search on new input', fakeAsync(() => {\n const slow$ = of([{ id: 1 }]).pipe(delay(500));\n const fast$ = of([{ id: 2 }]);\n searchService.search.and.returnValues(slow$, fast$);\n\n component.searchControl.setValue('first');\n tick(300);\n component.searchControl.setValue('second');\n tick(300);\n tick(500);\n\n expect(component.results.length).toBe(1);\n expect(component.results[0].id).toBe(2);\n\n discardPeriodicTimers();\n }));\n\n it('should unsubscribe on destroy', () => {\n const subject = new Subject();\n spyOn(subject, 'unsubscribe');\n component['subscription'] = subject.subscribe();\n component.ngOnDestroy();\n expect(component['subscription'].closed).toBeTrue();\n });\n});\n```\n\n---\n\n## §6 Router & NgRx Testing\n\n### Router Testing\n```typescript\nimport { RouterTestingModule } from '@angular/router/testing';\nimport { Router } from '@angular/router';\nimport { Location } from '@angular/common';\n\ndescribe('AppComponent routing', () => {\n let router: Router;\n let location: Location;\n\n beforeEach(async () => {\n await TestBed.configureTestingModule({\n imports: [\n RouterTestingModule.withRoutes([\n { path: '', component: HomeComponent },\n { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },\n { path: '**', component: NotFoundComponent },\n ]),\n ],\n declarations: [AppComponent, HomeComponent, DashboardComponent, NotFoundComponent],\n providers: [{ provide: AuthGuard, useValue: { canActivate: () => true } }],\n }).compileComponents();\n\n router = TestBed.inject(Router);\n location = TestBed.inject(Location);\n router.initialNavigation();\n });\n\n it('should navigate to dashboard', fakeAsync(() => {\n router.navigate(['/dashboard']);\n tick();\n expect(location.path()).toBe('/dashboard');\n }));\n\n it('should redirect unknown routes to 404', fakeAsync(() => {\n router.navigate(['/nonexistent']);\n tick();\n expect(location.path()).toBe('/nonexistent');\n }));\n});\n```\n\n### NgRx Store Testing\n```typescript\nimport { provideMockStore, MockStore } from '@ngrx/store/testing';\nimport { Store } from '@ngrx/store';\nimport * as UserActions from '../store/user.actions';\nimport * as UserSelectors from '../store/user.selectors';\n\ndescribe('UserListComponent with NgRx', () => {\n let store: MockStore;\n let fixture: ComponentFixture\u003cUserListComponent>;\n\n const initialState = {\n users: {\n list: [],\n loading: false,\n error: null,\n },\n };\n\n beforeEach(async () => {\n await TestBed.configureTestingModule({\n declarations: [UserListComponent],\n providers: [\n provideMockStore({\n initialState,\n selectors: [\n { selector: UserSelectors.selectAllUsers, value: [] },\n { selector: UserSelectors.selectLoading, value: false },\n ],\n }),\n ],\n }).compileComponents();\n\n store = TestBed.inject(Store) as MockStore;\n spyOn(store, 'dispatch');\n fixture = TestBed.createComponent(UserListComponent);\n fixture.detectChanges();\n });\n\n it('should dispatch loadUsers on init', () => {\n expect(store.dispatch).toHaveBeenCalledWith(UserActions.loadUsers());\n });\n\n it('should show loading spinner', () => {\n store.overrideSelector(UserSelectors.selectLoading, true);\n store.refreshState();\n fixture.detectChanges();\n\n const spinner = fixture.nativeElement.querySelector('[data-testid=\"loading\"]');\n expect(spinner).toBeTruthy();\n });\n\n it('should render users from store', () => {\n store.overrideSelector(UserSelectors.selectAllUsers, [\n { id: 1, name: 'Alice' },\n { id: 2, name: 'Bob' },\n ]);\n store.refreshState();\n fixture.detectChanges();\n\n const rows = fixture.nativeElement.querySelectorAll('[data-testid=\"user-row\"]');\n expect(rows.length).toBe(2);\n });\n});\n```\n\n---\n\n## §7 LambdaTest Cloud Integration\n\n```javascript\n// karma.conf.js — LambdaTest cloud browsers\nmodule.exports = function (config) {\n const customLaunchers = {\n lt_chrome_win: {\n base: 'WebDriver',\n config: { hostname: 'hub.lambdatest.com', port: 80 },\n browserName: 'Chrome',\n version: 'latest',\n platform: 'Windows 11',\n name: 'Karma Chrome Win',\n tunnel: true,\n tunnelName: 'karma-tunnel',\n user: process.env.LT_USERNAME,\n accessKey: process.env.LT_ACCESS_KEY,\n video: true,\n console: true,\n },\n lt_firefox_mac: {\n base: 'WebDriver',\n config: { hostname: 'hub.lambdatest.com', port: 80 },\n browserName: 'Firefox',\n version: 'latest',\n platform: 'macOS Sonoma',\n name: 'Karma Firefox Mac',\n tunnel: true,\n user: process.env.LT_USERNAME,\n accessKey: process.env.LT_ACCESS_KEY,\n },\n };\n\n config.set({\n browsers: Object.keys(customLaunchers),\n customLaunchers,\n concurrency: 2,\n browserDisconnectTimeout: 60000,\n browserNoActivityTimeout: 120000,\n });\n};\n```\n\n---\n\n## §8 CI/CD Integration\n\n### GitHub Actions\n```yaml\nname: Angular Tests\non:\n push:\n branches: [main, develop]\n pull_request:\n branches: [main]\n\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - uses: actions/setup-node@v4\n with:\n node-version: 20\n cache: 'npm'\n\n - run: npm ci\n\n - run: npx ng test --watch=false --code-coverage --browsers=ChromeHeadlessNoSandbox\n env:\n CI: true\n\n - name: Upload Coverage\n uses: actions/upload-artifact@v4\n with:\n name: coverage-report\n path: coverage/\n\n - name: Check Coverage Threshold\n run: |\n COVERAGE=$(cat coverage/text-summary.txt | grep 'Lines' | grep -oP '[\\d.]+%' | head -1)\n echo \"Line coverage: $COVERAGE\"\n\n - name: Publish Test Results\n uses: dorny/test-reporter@v1\n if: always()\n with:\n name: Karma Test Results\n path: test-results/karma-results.xml\n reporter: java-junit\n```\n\n---\n\n## §9 Debugging Table\n\n| # | Problem | Cause | Fix |\n|---|---------|-------|-----|\n| 1 | `No captured browser` | Browser fails to launch in CI | Use `ChromeHeadlessNoSandbox` with `--no-sandbox --disable-gpu` flags |\n| 2 | `Disconnected: no message in 60000 ms` | Tests take too long or browser hangs | Increase `browserNoActivityTimeout` and `browserDisconnectTimeout` |\n| 3 | `Expected spy to have been called` | Async code not flushed | Wrap in `fakeAsync` + `tick()` or `flush()` |\n| 4 | `Cannot read properties of null` | DOM element not rendered | Call `fixture.detectChanges()` before querying DOM |\n| 5 | `NullInjectorError: No provider for X` | Dependency not provided in TestBed | Add missing `providers` or `imports` in `TestBed.configureTestingModule()` |\n| 6 | `1 periodic timer(s) still in queue` | Interval/timer not cleaned up | Call `discardPeriodicTimers()` at end of `fakeAsync` test |\n| 7 | `HttpTestingController: Unflushed requests` | HTTP mock not responded | Call `req.flush()` for every expected request; `httpMock.verify()` in `afterEach` |\n| 8 | Coverage shows 0% on all files | Preprocessor misconfigured | Ensure `coverage` preprocessor is in `karma.conf.js` and files match patterns |\n| 9 | Tests pass locally, fail in CI | Browser difference or timing | Use `ChromeHeadless` in CI; add explicit waits; check `fakeAsync` usage |\n| 10 | `ExpressionChangedAfterItHasBeenCheckedError` | Change detection triggered after check | Call `fixture.detectChanges()` after changing component state |\n| 11 | `Cannot configure TestBed after initialized` | TestBed not reset | Ensure each `describe` has its own `beforeEach` with `TestBed.configureTestingModule` |\n| 12 | Spies not resetting between tests | Spy created outside beforeEach | Create all `jasmine.createSpyObj` inside `beforeEach`; Jasmine auto-resets per test |\n\n---\n\n## §10 Best Practices Checklist\n\n1. Use `ChromeHeadlessNoSandbox` for CI — never run headed Chrome in pipelines\n2. Use `fakeAsync`/`tick` for all async Angular tests — never use `done()` callback\n3. Call `fixture.detectChanges()` after every state change before asserting DOM\n4. Call `httpMock.verify()` in `afterEach` — catch unhandled HTTP requests\n5. Use `data-testid` attributes for DOM queries — not CSS classes or tag names\n6. Create spies in `beforeEach` — let Jasmine auto-reset them per test\n7. Set `failSpecWithNoExpectations: true` — catch tests with missing assertions\n8. Use `discardPeriodicTimers()` — clean up intervals in fakeAsync tests\n9. Set coverage thresholds in `karma.conf.js` — fail build on coverage drops\n10. Use `RouterTestingModule` — never import real routing module in tests\n11. Use `provideMockStore` for NgRx — isolate component from store side effects\n12. Keep tests under 50 lines — extract helpers for common setup patterns\n13. Use `jasmine.objectContaining()` for partial assertion matching\n14. Run with `--random-order` — catch test interdependencies early\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":22990,"content_sha256":"690cfcdca68b6de472f840e50022e0bd962eb90663afc91fc2461b962c27be54"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Karma Testing Skill","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Patterns","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"karma.conf.js","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"module.exports = function(config) {\n config.set({\n basePath: '',\n frameworks: ['jasmine'],\n files: [\n 'src/**/*.js',\n 'test/**/*.spec.js'\n ],\n preprocessors: {\n 'src/**/*.js': ['coverage']\n },\n reporters: ['progress', 'coverage'],\n coverageReporter: {\n type: 'html',\n dir: 'coverage/'\n },\n port: 9876,\n colors: true,\n logLevel: config.LOG_INFO,\n autoWatch: true,\n browsers: ['Chrome', 'Firefox'],\n singleRun: false,\n concurrency: Infinity,\n\n // LambdaTest cloud browsers\n customLaunchers: {\n ChromeLT: {\n base: 'WebDriver',\n config: {\n hostname: 'hub.lambdatest.com',\n port: 80\n },\n browserName: 'Chrome',\n version: 'latest',\n name: 'Karma Test',\n tunnel: true,\n user: process.env.LT_USERNAME,\n accessKey: process.env.LT_ACCESS_KEY\n }\n }\n });\n};","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Test with Jasmine Framework","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"describe('StringUtils', () => {\n it('should capitalize first letter', () => {\n expect(StringUtils.capitalize('hello')).toBe('Hello');\n });\n\n it('should handle empty string', () => {\n expect(StringUtils.capitalize('')).toBe('');\n });\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Angular Integration","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"// karma.conf.js for Angular\nframeworks: ['jasmine', '@angular-devkit/build-angular'],\nplugins: [\n require('karma-jasmine'),\n require('karma-chrome-launcher'),\n require('karma-coverage'),\n require('@angular-devkit/build-angular/plugins/karma')\n],","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Setup: ","type":"text"},{"text":"npm install karma karma-jasmine karma-chrome-launcher karma-coverage --save-dev","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Run: ","type":"text"},{"text":"npx karma start","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"npx karma start --single-run","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Init: ","type":"text"},{"text":"npx karma init karma.conf.js","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Deep Patterns","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"reference/playbook.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for production-grade patterns:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Section","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"What You Get","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§1 Production Configuration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full karma.conf.js with coverage thresholds, reporters, CI launchers","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§2 Component Testing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Service mocking, DOM interaction, form validation patterns","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§3 HTTP Service Testing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HttpTestingController, error handling, retry testing","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§4 Directive & Pipe Testing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Host component pattern, custom pipes with edge cases","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§5 RxJS & Async Patterns","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"debounceTime, switchMap cancellation, subscription cleanup","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§6 Router & NgRx Testing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"RouterTestingModule, MockStore, selector overrides","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§7 LambdaTest Integration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cloud browser configuration for cross-browser testing","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§8 CI/CD Integration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GitHub Actions with coverage, test reporting","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§9 Debugging Table","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"12 common problems with causes and fixes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§10 Best Practices","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"14-item checklist for production Angular testing","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"karma-skill","author":"@skillopedia","source":{"stars":302,"repo_name":"agent-skills","origin_url":"https://github.com/lambdatest/agent-skills/blob/HEAD/karma-skill/SKILL.md","repo_owner":"lambdatest","body_sha256":"fa2b8756c0d41526b9fd0e44b8e03b1a5a7c3deb8f21ecfaf3635afba3085097","cluster_key":"3e8b8e27ddd3a7b06bc331e28f80681d54b7a065bd9a44d81970c5dba67d10fa","clean_bundle":{"format":"clean-skill-bundle-v1","source":"lambdatest/agent-skills/karma-skill/SKILL.md","attachments":[{"id":"eb766476-d20e-5798-b57e-a2377c93d5e7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/eb766476-d20e-5798-b57e-a2377c93d5e7/attachment.md","path":"reference/advanced-patterns.md","size":3162,"sha256":"7d25b06549546fb926a5036ad6a6f69e9adf2b9207f2a1a3ef1746e9db9f84b3","contentType":"text/markdown; charset=utf-8"},{"id":"53ffc4b6-4ac0-5d66-ab3a-40e4d1e4a5ef","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/53ffc4b6-4ac0-5d66-ab3a-40e4d1e4a5ef/attachment.md","path":"reference/playbook.md","size":22990,"sha256":"690cfcdca68b6de472f840e50022e0bd962eb90663afc91fc2461b962c27be54","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"9f23fff53a7db670db68a194df27fb6983a4f338744f938c7b736b5b06cffc49","attachment_count":2,"text_attachments":2,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"karma-skill/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"browser-automation-scraping","category_label":"Browser"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"browser-automation-scraping","metadata":{"author":"TestMu AI","version":"1.0"},"languages":["JavaScript","TypeScript"],"import_tag":"clean-skills-v1","description":"Generates Karma test runner configurations for browser-based JavaScript testing. Works with Jasmine, Mocha, or QUnit. Use when user mentions \"Karma\", \"karma.conf.js\", \"browser test runner\". Triggers on: \"Karma\", \"karma.conf\", \"karma test runner\", \"browser-based JS test\".\n"}},"renderedAt":1782980217785}

Karma Testing Skill Core Patterns karma.conf.js Test with Jasmine Framework Angular Integration Setup: Run: or Init: Deep Patterns See for production-grade patterns: | Section | What You Get | |---------|-------------| | §1 Production Configuration | Full karma.conf.js with coverage thresholds, reporters, CI launchers | | §2 Component Testing | Service mocking, DOM interaction, form validation patterns | | §3 HTTP Service Testing | HttpTestingController, error handling, retry testing | | §4 Directive & Pipe Testing | Host component pattern, custom pipes with edge cases | | §5 RxJS & Async Pat…