{"id":"a9fc68b8-b551-48e8-a82b-2d40ea0402c1","shortId":"Vdbk77","kind":"skill","title":"cometchat-angular-testing","tagline":"Testing patterns for CometChat Angular UI Kit v4 in Angular 12-15 projects. Covers Karma (default Angular test runner) and Jest (jest-preset-angular alternative), TestBed configuration with CometChatUIKitModule mocking, NgZone-aware async assertions, Cypress for e2e flows with We","description":"## Purpose\n\nTest recipes for CometChat Angular UI Kit integrations. Covers both Angular runners (Karma — default; Jest — preferred by many teams), TestBed patterns specific to NgZone-driven components, and Cypress for full e2e.\n\n**Read these other skills first:**\n- `cometchat-angular-core` — init service, APP_INITIALIZER pattern\n- `cometchat-angular-patterns` — module setup, lazy loading\n- `cometchat-angular-calls/references/ngzone-and-async-callbacks.md` — NgZone primer (carries over to test patterns)\n\n**Ground truth:**\n- Angular testing — https://angular.io/guide/testing\n- jest-preset-angular — https://github.com/thymikee/jest-preset-angular\n- Cypress for Angular — https://docs.cypress.io/guides/component-testing/angular/overview\n\n---\n\n## 1. Karma vs Jest — pick one\n\n| Criterion | Karma (default) | Jest |\n|---|---|---|\n| Comes with `ng new` | ✓ | ✗ (manual setup) |\n| Speed | Slower (real browser) | Faster (jsdom) |\n| Real browser parity | Higher | Lower (no real CSS, no real layout) |\n| Snapshot testing | Possible (jasmine-jest-snapshot) | First-class |\n| CI complexity | Need ChromeHeadless | Pure Node |\n\nThe skill defaults to Karma if the project already uses it, Jest if greenfield.\n\n### Karma setup (default Angular)\n\nAlready configured by `ng new`. The skill writes test files only.\n\n### Jest setup\n\n```bash\nnpm install -D jest @types/jest jest-preset-angular @angular-builders/jest\nng add @angular-builders/jest\n```\n\n`jest.config.js`:\n\n```js\nmodule.exports = {\n  preset: \"jest-preset-angular\",\n  setupFilesAfterEach: [\"<rootDir>/setup-jest.ts\"],\n  globalSetup: \"jest-preset-angular/global-setup\",\n  testPathIgnorePatterns: [\"/node_modules/\", \"/dist/\", \"/cypress/\"],\n};\n```\n\n`setup-jest.ts`:\n\n```ts\nimport \"jest-preset-angular/setup-jest\";\n```\n\n`angular.json` test command becomes `ng test --code-coverage`.\n\n---\n\n## 2. TestBed setup with CometChat module\n\n```ts\n// chat.component.spec.ts\nimport { ComponentFixture, TestBed } from \"@angular/core/testing\";\nimport { CUSTOM_ELEMENTS_SCHEMA } from \"@angular/core\";\nimport { ChatComponent } from \"./chat.component\";\nimport { CometChatInitService } from \"../services/cometchat-init.service\";\n\ndescribe(\"ChatComponent\", () => {\n  let fixture: ComponentFixture<ChatComponent>;\n  let component: ChatComponent;\n  let initService: jasmine.SpyObj<CometChatInitService>;\n\n  beforeEach(async () => {\n    initService = jasmine.createSpyObj(\"CometChatInitService\", [\"init\", \"login\"]);\n    initService.init.and.returnValue(Promise.resolve());\n    initService.login.and.returnValue(Promise.resolve({ uid: \"cometchat-uid-1\" }));\n\n    await TestBed.configureTestingModule({\n      declarations: [ChatComponent],\n      providers: [\n        { provide: CometChatInitService, useValue: initService },\n      ],\n      schemas: [CUSTOM_ELEMENTS_SCHEMA],          // critical — UI Kit selectors\n    }).compileComponents();\n\n    fixture = TestBed.createComponent(ChatComponent);\n    component = fixture.componentInstance;\n  });\n\n  it(\"calls init on construction\", () => {\n    fixture.detectChanges();\n    expect(initService.init).toHaveBeenCalled();\n  });\n});\n```\n\n`CUSTOM_ELEMENTS_SCHEMA` makes the test ignore unknown elements (the `<cometchat-*>` selectors in templates). Without it, every test fails with \"is not a known element.\"\n\n---\n\n## 3. NgZone + fakeAsync for SDK callbacks\n\nThe Calls SDK fires callbacks outside Angular's zone (cf. `cometchat-angular-calls/references/ngzone-and-async-callbacks.md`). Tests for components subscribing to those callbacks need `fakeAsync` + `tick`:\n\n```ts\nimport { fakeAsync, tick } from \"@angular/core/testing\";\n\nit(\"renders error when SDK fails\", fakeAsync(() => {\n  initService.login.and.returnValue(Promise.reject(new Error(\"401 Unauthorized\")));\n\n  fixture.detectChanges();\n  tick();                                          // flush pending promises\n  fixture.detectChanges();                         // re-render after state change\n\n  const errorEl = fixture.nativeElement.querySelector(\".error-message\");\n  expect(errorEl.textContent).toContain(\"401\");\n}));\n```\n\nFor SDK callbacks dispatched via `setTimeout` or `requestAnimationFrame`, `tick(N)` advances by N ms.\n\n---\n\n## 4. Mocking `@cometchat/chat-uikit-angular`\n\nThe UI Kit is heavy — full module imports inflate test bundles and trigger zone-related warnings in jsdom. Mock at the module level:\n\n```ts\n// __mocks__/cometchat-uikit-angular.ts\nimport { Component, NgModule, NO_ERRORS_SCHEMA } from \"@angular/core\";\n\n@Component({ selector: \"cometchat-conversations\", template: \"<div>conversations</div>\" })\nexport class CometChatConversationsStub {}\n\n@Component({ selector: \"cometchat-message-list\", template: \"<div>message-list</div>\" })\nexport class CometChatMessageListStub {}\n\n@Component({ selector: \"cometchat-message-composer\", template: \"<div>composer</div>\" })\nexport class CometChatMessageComposerStub {}\n\n@Component({ selector: \"cometchat-message-header\", template: \"<div>header</div>\" })\nexport class CometChatMessageHeaderStub {}\n\n@NgModule({\n  declarations: [\n    CometChatConversationsStub,\n    CometChatMessageListStub,\n    CometChatMessageComposerStub,\n    CometChatMessageHeaderStub,\n  ],\n  exports: [\n    CometChatConversationsStub,\n    CometChatMessageListStub,\n    CometChatMessageComposerStub,\n    CometChatMessageHeaderStub,\n  ],\n})\nexport class CometChatUIKitModuleStub {}\n```\n\nIn a test:\n\n```ts\nawait TestBed.configureTestingModule({\n  declarations: [ChatComponent],\n  imports: [CometChatUIKitModuleStub],\n  providers: [/* ... */],\n}).compileComponents();\n```\n\nFor Jest, alias the module path:\n\n```js\n// jest.config.js\nmoduleNameMapper: {\n  \"^@cometchat/chat-uikit-angular$\": \"<rootDir>/__mocks__/cometchat-uikit-angular.ts\",\n}\n```\n\nFor Karma, less straightforward — typically you import the stub module directly in the test file and let TypeScript's structural typing handle it.\n\n---\n\n## 5. Standalone components (Angular 14+)\n\n```ts\nimport { ComponentFixture, TestBed } from \"@angular/core/testing\";\nimport { ChatComponent } from \"./chat.component\";\n\ndescribe(\"ChatComponent (standalone)\", () => {\n  let fixture: ComponentFixture<ChatComponent>;\n\n  beforeEach(async () => {\n    await TestBed.configureTestingModule({\n      imports: [ChatComponent],                   // standalone components imported, not declared\n      providers: [{ provide: CometChatInitService, useValue: { init: () => Promise.resolve() } }],\n    }).compileComponents();\n\n    fixture = TestBed.createComponent(ChatComponent);\n  });\n});\n```\n\n---\n\n## 6. Cypress e2e\n\n### Install\n\n```bash\nnpm install -D cypress\nnpx cypress open                                  # generates cypress/ scaffold\n```\n\n### `cypress.config.ts`\n\n```ts\nimport { defineConfig } from \"cypress\";\n\nexport default defineConfig({\n  e2e: {\n    baseUrl: process.env.E2E_BASE_URL ?? \"http://localhost:4200\",\n    supportFile: \"cypress/support/e2e.ts\",\n    specPattern: \"cypress/e2e/**/*.cy.ts\",\n    defaultCommandTimeout: 10_000,                // generous for WebSocket-driven assertions\n  },\n});\n```\n\n### Two-window chat smoke\n\nCypress doesn't natively support multi-window tests. Workaround: open a second browser context via Playwright OR test with two iframes. Most teams use Playwright for cross-window tests — keep Cypress for single-window smoke.\n\n```ts\n// cypress/e2e/chat.cy.ts\ndescribe(\"chat smoke\", () => {\n  it(\"logs in and sees the conversation list\", () => {\n    cy.visit(\"/\");\n    cy.window().then((win) => {\n      win.localStorage.setItem(\"cc-test-uid\", \"cometchat-uid-1\");\n    });\n    cy.reload();\n\n    cy.contains(\"Welcome\").should(\"be.visible\");\n    cy.visit(\"/messages\");\n    cy.contains(\"Conversations\", { timeout: 10_000 }).should(\"be.visible\");\n  });\n\n  it(\"sends a message and sees it in the thread\", () => {\n    cy.visit(\"/messages\");\n    cy.contains(\"cometchat-uid-2\").click();\n    cy.get(\"input[placeholder='Type a message']\").type(\"Hello\");\n    cy.contains(\"Send\").click();\n    cy.contains(\"Hello\", { timeout: 5_000 }).should(\"be.visible\");\n  });\n});\n```\n\nFor two-user tests, prefer Playwright — see `cometchat-react-testing/SKILL.md`.\n\n---\n\n## 7. Anti-patterns\n\n1. **Skipping `CUSTOM_ELEMENTS_SCHEMA`** in TestBed. Every test fails with \"is not a known element\" for `<cometchat-*>` selectors.\n2. **Using `ngOnInit()` directly in tests.** Always go through `fixture.detectChanges()` so Angular's lifecycle runs the way it does in production.\n3. **Mocking `CometChat.init`** with a synchronous return value. Real init returns a Promise; sync mocks mask race conditions in your code.\n4. **`async` test bodies without `await`** on the assertion. The test passes (trivially) because the assertion never ran. Use `fakeAsync` + `tick` or proper `await fixture.whenStable()`.\n5. **Hardcoding test user UIDs** in templates. Use a service injection so tests can override.\n6. **Running Karma in CI without `--watch=false --browsers=ChromeHeadless`.** Default `ng test` watches forever; CI hangs.\n\n## 8. CI configuration\n\n```yaml\n# .github/workflows/test.yml\nname: tests\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with: { node-version: 20, cache: npm }\n      - run: npm ci\n\n      # Karma\n      - run: npm run test -- --watch=false --browsers=ChromeHeadless --code-coverage\n      # OR Jest\n      # - run: npm run test -- --ci --coverage\n\n      - run: npx cypress run\n        env:\n          E2E_BASE_URL: http://localhost:4200\n          # ng serve in background:\n          # - run: npx concurrently \"ng serve\" \"wait-on http://localhost:4200 && npx cypress run\"\n```\n\nUse a dedicated test CometChat App ID in CI — never share with production.\n\n---\n\n## 9. Verification checklist\n\n- [ ] `karma.conf.js` (Angular default) OR `jest.config.js` (Jest path) configured\n- [ ] At least one test using `CUSTOM_ELEMENTS_SCHEMA` for components that render `<cometchat-*>`\n- [ ] At least one test asserting \"init runs and resolves before component is usable\"\n- [ ] At least one test asserting \"error state renders when SDK throws\"\n- [ ] `CometChatInitService` mocked, not the real one\n- [ ] No hardcoded App ID / Auth Key in test files\n- [ ] CI runs unit + e2e separately with dedicated test app credentials\n- [ ] `--watch=false` flag in CI Karma command (otherwise hangs)\n\n## 10. Pointers\n\n- `cometchat-angular-calls/references/ngzone-and-async-callbacks.md` — calls testing\n- `cometchat-angular-core` — init service to mock against\n- `cometchat-angular-troubleshooting` — common test failures","tags":["cometchat","angular","testing","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-angular-testing","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-angular-testing","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (10,515 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:48.662Z","embedding":null,"createdAt":"2026-05-18T07:04:20.551Z","updatedAt":"2026-05-18T19:04:48.662Z","lastSeenAt":"2026-05-18T19:04:48.662Z","tsv":"'-15':16 '/__mocks__/cometchat-uikit-angular.ts':587 '/chat.component':293,625 '/cometchat-uikit-angular.ts':497 '/cypress':253 '/dist':252 '/global-setup':249 '/guide/testing':120 '/guides/component-testing/angular/overview':133 '/jest':227,233 '/messages':774,793 '/node_modules':251 '/references/ngzone-and-async-callbacks.md':106,402,1137 '/services/cometchat-init.service':297 '/setup-jest':261 '/setup-jest.ts':243 '/skill.md':830 '/thymikee/jest-preset-angular':127 '000':692,779,815 '1':134,324,767,835 '10':691,778,1131 '12':15 '14':615 '2':271,798,854 '20':983 '3':382,875 '4':468,896 '401':430,453 '4200':684,1018,1032 '5':611,814,921 '6':653,936 '7':831 '8':953 '9':1049 'actions/checkout':974 'actions/setup-node':977 'add':229 'advanc':464 'alia':579 'alreadi':191,201 'altern':30 'alway':860 'angular':3,9,14,21,29,52,58,87,96,104,116,124,130,200,223,225,231,241,248,260,394,400,614,865,1053,1135,1142,1151 'angular-build':224,230 'angular.io':119 'angular.io/guide/testing':118 'angular.json':262 'angular/core':289,505 'angular/core/testing':283,418,621 'anti':833 'anti-pattern':832 'app':91,1041,1105,1120 'assert':40,698,904,911,1077,1090 'async':39,310,633,897 'auth':1107 'await':325,569,634,901,919 'awar':38 'background':1022 'base':681,1015 'baseurl':678 'bash':214,657 'be.visible':772,781,817 'becom':265 'beforeeach':309,632 'bodi':899 'browser':153,157,717,944,996 'builder':226,232 'bundl':481 'cach':984 'call':105,349,389,401,1136,1138 'callback':387,392,409,456 'carri':109 'cc':761 'cc-test-uid':760 'cf':397 'chang':443 'chat':702,745 'chat.component.spec.ts':278 'chatcompon':291,299,305,328,345,572,623,627,637,652 'checklist':1051 'chromeheadless':180,945,997 'ci':177,940,951,954,988,1007,1044,1112,1126 'class':176,514,527,538,549,563 'click':799,810 'code':269,895,999 'code-coverag':268,998 'come':144 'cometchat':2,8,51,86,95,103,275,322,367,399,509,519,532,543,765,796,827,852,1040,1072,1134,1141,1150 'cometchat-angular-cal':102,398,1133 'cometchat-angular-cor':85,1140 'cometchat-angular-pattern':94 'cometchat-angular-test':1 'cometchat-angular-troubleshoot':1149 'cometchat-convers':508 'cometchat-message-compos':531 'cometchat-message-head':542 'cometchat-message-list':518 'cometchat-react-test':826 'cometchat-uid':321,764,795 'cometchat.init':877 'cometchat/chat-uikit-angular':470,586 'cometchatconversationsstub':515,553,558 'cometchatinitservic':295,313,331,645,1097 'cometchatmessagecomposerstub':539,555,560 'cometchatmessageheaderstub':550,556,561 'cometchatmessageliststub':528,554,559 'cometchatuikitmodul':34 'cometchatuikitmodulestub':564,574 'command':264,1128 'common':1153 'compilecompon':342,576,649 'complex':178 'compon':74,304,346,405,499,506,516,529,540,613,639,1069,1083 'componentfixtur':280,302,618,631 'compos':534,536 'concurr':1025 'condit':892 'configur':32,202,955,1059 'const':444 'construct':352 'context':718 'convers':510,512,753,776 'core':88,1143 'cover':18,56 'coverag':270,1000,1008 'credenti':1121 'criterion':140 'critic':338 'cross':732 'cross-window':731 'css':163 'custom':285,335,357,837,1065 'cy.contains':769,775,794,808,811 'cy.get':800 'cy.reload':768 'cy.ts':689 'cy.visit':755,773,792 'cy.window':756 'cypress':41,76,128,654,661,663,666,673,704,736,1011,1034 'cypress.config.ts':668 'cypress/e2e':688 'cypress/e2e/chat.cy.ts':743 'cypress/support/e2e.ts':686 'd':217,660 'declar':327,552,571,642 'dedic':1038,1118 'default':20,61,142,185,199,675,946,1054 'defaultcommandtimeout':690 'defineconfig':671,676 'describ':298,626,744 'direct':598,857 'dispatch':457 'docs.cypress.io':132 'docs.cypress.io/guides/component-testing/angular/overview':131 'doesn':705 'driven':73,697 'e2e':43,79,655,677,680,1014,1115 'element':286,336,358,365,381,838,850,1066 'env':1013 'error':421,429,448,502,1091 'error-messag':447 'errorel':445 'errorel.textcontent':451 'everi':373,842 'expect':354,450 'export':513,526,537,548,557,562,674 'fail':375,424,844 'failur':1155 'fakeasync':384,411,415,425,915 'fals':943,995,1123 'faster':154 'file':210,602,1111 'fire':391 'first':84,175 'first-class':174 'fixtur':301,343,630,650 'fixture.componentinstance':347 'fixture.detectchanges':353,432,437,863 'fixture.nativeelement.queryselector':446 'fixture.whenstable':920 'flag':1124 'flow':44 'flush':434 'forev':950 'full':78,476 'generat':665 'generous':693 'github.com':126 'github.com/thymikee/jest-preset-angular':125 'github/workflows/test.yml':957 'globalsetup':244 'go':861 'greenfield':196 'ground':114 'handl':609 'hang':952,1130 'hardcod':922,1104 'header':545,547 'heavi':475 'hello':807,812 'higher':159 'id':1042,1106 'ifram':725 'ignor':363 'import':256,279,284,290,294,414,478,498,573,594,617,622,636,640,670 'inflat':479 'init':89,314,350,647,884,1078,1144 'initi':92 'initservic':307,311,333 'initservice.init':355 'initservice.init.and.returnvalue':316 'initservice.login.and.returnvalue':318,426 'inject':931 'input':801 'instal':216,656,659 'integr':55 'jasmin':171 'jasmine-jest-snapshot':170 'jasmine.createspyobj':312 'jasmine.spyobj':308 'jest':25,27,62,122,137,143,172,194,212,218,221,239,246,258,578,1002,1057 'jest-preset-angular':26,121,220,238,245,257 'jest.config.js':234,584,1056 'job':964 'js':235,583 'jsdom':155,489 'karma':19,60,135,141,187,197,589,938,989,1127 'karma.conf.js':1052 'keep':735 'key':1108 'kit':11,54,340,473 'known':380,849 'latest':971 'layout':166 'lazi':100 'least':1061,1074,1087 'less':590 'let':300,303,306,604,629 'level':494 'lifecycl':867 'list':521,525,754 'load':101 'localhost':683,1017,1031 'log':748 'login':315 'lower':160 'make':360 'mani':65 'manual':148 'mask':890 'messag':449,520,524,533,544,785,805 'message-list':523 'mock':35,469,490,496,876,889,1098,1147 'modul':98,276,477,493,581,597 'module.exports':236 'modulenamemapp':585 'ms':467 'multi':710 'multi-window':709 'n':463,466 'name':958 'nativ':707 'need':179,410 'never':912,1045 'new':147,205,428 'ng':146,204,228,266,947,1019,1026 'ngmodul':500,551 'ngoninit':856 'ngzone':37,72,107,383 'ngzone-awar':36 'ngzone-driven':71 'node':182,981 'node-vers':980 'npm':215,658,985,987,991,1004 'npx':662,1010,1024,1033 'one':139,1062,1075,1088,1102 'open':664,714 'otherwis':1129 'outsid':393 'overrid':935 'pariti':158 'pass':907 'path':582,1058 'pattern':6,68,93,97,113,834 'pend':435 'pick':138 'placehold':802 'playwright':720,729,824 'pointer':1132 'possibl':169 'prefer':63,823 'preset':28,123,222,237,240,247,259 'primer':108 'process.env':679 'product':874,1048 'project':17,190 'promis':436,887 'promise.reject':427 'promise.resolve':317,319,648 'proper':918 'provid':329,330,575,643,644 'pull':962 'pure':181 'purpos':47 'push':961 'race':891 'ran':913 're':439 're-rend':438 'react':828 'read':80 'real':152,156,162,165,883,1101 'recip':49 'relat':486 'render':420,440,1071,1093 'request':963 'requestanimationfram':461 'resolv':1081 'return':881,885 'run':868,937,967,986,990,992,1003,1005,1009,1012,1023,1035,1079,1113 'runner':23,59 'runs-on':966 'scaffold':667 'schema':287,334,337,359,503,839,1067 'sdk':386,390,423,455,1095 'second':716 'see':751,787,825 'selector':341,368,507,517,530,541,853 'send':783,809 'separ':1116 'serv':1020,1027 'servic':90,930,1145 'settimeout':459 'setup':99,149,198,213,273 'setup-jest.ts':254 'setupfilesaftereach':242 'share':1046 'singl':739 'single-window':738 'skill':83,184,207 'skill-cometchat-angular-testing' 'skip':836 'slower':151 'smoke':703,741,746 'snapshot':167,173 'source-cometchat' 'specif':69 'specpattern':687 'speed':150 'standalon':612,628,638 'state':442,1092 'step':972 'straightforward':591 'structur':607 'stub':596 'subscrib':406 'support':708 'supportfil':685 'sync':888 'synchron':880 'team':66,727 'templat':370,511,522,535,546,927 'test':4,5,22,48,112,117,168,209,263,267,362,374,403,480,567,601,712,722,734,762,822,829,843,859,898,906,923,933,948,959,965,993,1006,1039,1063,1076,1089,1110,1119,1139,1154 'testb':31,67,272,281,619,841 'testbed.configuretestingmodule':326,570,635 'testbed.createcomponent':344,651 'testpathignorepattern':250 'thread':791 'throw':1096 'tick':412,416,433,462,916 'timeout':777,813 'tocontain':452 'tohavebeencal':356 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'trigger':483 'trivial':908 'troubleshoot':1152 'truth':115 'ts':255,277,413,495,568,616,669,742 'two':700,724,820 'two-us':819 'two-window':699 'type':608,803,806 'types/jest':219 'typescript':605 'typic':592 'ubuntu':970 'ubuntu-latest':969 'ui':10,53,339,472 'uid':320,323,763,766,797,925 'unauthor':431 'unit':1114 'unknown':364 'url':682,1016 'usabl':1085 'use':192,728,855,914,928,973,976,1036,1064 'user':821,924 'usevalu':332,646 'v4':12,975,978 'valu':882 'verif':1050 'version':982 'via':458,719 'vs':136 'wait':1029 'wait-on':1028 'warn':487 'watch':942,949,994,1122 'way':870 'websocket':696 'websocket-driven':695 'welcom':770 'win':758 'win.localstorage.setitem':759 'window':701,711,733,740 'without':371,900,941 'workaround':713 'write':208 'yaml':956 'zone':396,485 'zone-rel':484","prices":[{"id":"0df50991-64a2-4fa9-bce9-81d94f07c43b","listingId":"a9fc68b8-b551-48e8-a82b-2d40ea0402c1","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T07:04:20.551Z"}],"sources":[{"listingId":"a9fc68b8-b551-48e8-a82b-2d40ea0402c1","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-angular-testing","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-angular-testing","isPrimary":false,"firstSeenAt":"2026-05-18T07:04:20.551Z","lastSeenAt":"2026-05-18T19:04:48.662Z"}],"details":{"listingId":"a9fc68b8-b551-48e8-a82b-2d40ea0402c1","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-angular-testing","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"f68f3d9dd8f988d87b98ebcca4e7bcc6240a1dc6","skill_md_path":"skills/cometchat-angular-testing/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-angular-testing"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-angular-testing","license":"MIT","description":"Testing patterns for CometChat Angular UI Kit v4 in Angular 12-15 projects. Covers Karma (default Angular test runner) and Jest (jest-preset-angular alternative), TestBed configuration with CometChatUIKitModule mocking, NgZone-aware async assertions, Cypress for e2e flows with WebSocket waits, and CI configuration. Sister skill of cometchat-angular-calls/references for the calls-specific testing patterns.","compatibility":"Angular 12-15 (LTS focus on 15); Karma + Jasmine (default) OR Jest + jest-preset-angular; Cypress >= 13; @cometchat/chat-uikit-angular ^4.x"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-angular-testing"},"updatedAt":"2026-05-18T19:04:48.662Z"}}