autoworkflow 3.1.5 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/analyze.md +19 -0
- package/.claude/commands/audit.md +26 -0
- package/.claude/commands/build.md +39 -0
- package/.claude/commands/commit.md +25 -0
- package/.claude/commands/fix.md +23 -0
- package/.claude/commands/plan.md +18 -0
- package/.claude/commands/suggest.md +23 -0
- package/.claude/commands/verify.md +18 -0
- package/.claude/hooks/post-bash-router.sh +20 -0
- package/.claude/hooks/post-commit.sh +140 -0
- package/.claude/hooks/pre-edit.sh +129 -0
- package/.claude/hooks/session-check.sh +79 -0
- package/.claude/settings.json +40 -6
- package/.claude/settings.local.json +3 -1
- package/.claude/skills/actix.md +337 -0
- package/.claude/skills/alembic.md +504 -0
- package/.claude/skills/angular.md +237 -0
- package/.claude/skills/api-design.md +187 -0
- package/.claude/skills/aspnet-core.md +377 -0
- package/.claude/skills/astro.md +245 -0
- package/.claude/skills/auth-clerk.md +327 -0
- package/.claude/skills/auth-firebase.md +367 -0
- package/.claude/skills/auth-nextauth.md +359 -0
- package/.claude/skills/auth-supabase.md +368 -0
- package/.claude/skills/axum.md +386 -0
- package/.claude/skills/blazor.md +456 -0
- package/.claude/skills/chi.md +348 -0
- package/.claude/skills/code-review.md +133 -0
- package/.claude/skills/csharp.md +296 -0
- package/.claude/skills/css-modules.md +325 -0
- package/.claude/skills/cypress.md +343 -0
- package/.claude/skills/debugging.md +133 -0
- package/.claude/skills/diesel.md +392 -0
- package/.claude/skills/django.md +301 -0
- package/.claude/skills/docker.md +319 -0
- package/.claude/skills/doctrine.md +473 -0
- package/.claude/skills/documentation.md +182 -0
- package/.claude/skills/dotnet.md +409 -0
- package/.claude/skills/drizzle.md +293 -0
- package/.claude/skills/echo.md +321 -0
- package/.claude/skills/eloquent.md +256 -0
- package/.claude/skills/emotion.md +426 -0
- package/.claude/skills/entity-framework.md +370 -0
- package/.claude/skills/express.md +316 -0
- package/.claude/skills/fastapi.md +329 -0
- package/.claude/skills/fastify.md +299 -0
- package/.claude/skills/fiber.md +315 -0
- package/.claude/skills/flask.md +322 -0
- package/.claude/skills/gin.md +342 -0
- package/.claude/skills/git.md +116 -0
- package/.claude/skills/github-actions.md +353 -0
- package/.claude/skills/go.md +377 -0
- package/.claude/skills/gorm.md +409 -0
- package/.claude/skills/graphql.md +478 -0
- package/.claude/skills/hibernate.md +379 -0
- package/.claude/skills/hono.md +306 -0
- package/.claude/skills/java.md +400 -0
- package/.claude/skills/jest.md +313 -0
- package/.claude/skills/jpa.md +282 -0
- package/.claude/skills/kotlin.md +347 -0
- package/.claude/skills/kubernetes.md +363 -0
- package/.claude/skills/laravel.md +414 -0
- package/.claude/skills/mcp-browser.md +320 -0
- package/.claude/skills/mcp-database.md +219 -0
- package/.claude/skills/mcp-fetch.md +241 -0
- package/.claude/skills/mcp-filesystem.md +204 -0
- package/.claude/skills/mcp-github.md +217 -0
- package/.claude/skills/mcp-memory.md +240 -0
- package/.claude/skills/mcp-search.md +218 -0
- package/.claude/skills/mcp-slack.md +262 -0
- package/.claude/skills/micronaut.md +388 -0
- package/.claude/skills/mongodb.md +319 -0
- package/.claude/skills/mongoose.md +355 -0
- package/.claude/skills/mysql.md +281 -0
- package/.claude/skills/nestjs.md +335 -0
- package/.claude/skills/nextjs-app-router.md +260 -0
- package/.claude/skills/nextjs-pages.md +172 -0
- package/.claude/skills/nuxt.md +202 -0
- package/.claude/skills/openapi.md +489 -0
- package/.claude/skills/performance.md +199 -0
- package/.claude/skills/php.md +398 -0
- package/.claude/skills/playwright.md +371 -0
- package/.claude/skills/postgresql.md +257 -0
- package/.claude/skills/prisma.md +293 -0
- package/.claude/skills/pydantic.md +304 -0
- package/.claude/skills/pytest.md +313 -0
- package/.claude/skills/python.md +272 -0
- package/.claude/skills/quarkus.md +377 -0
- package/.claude/skills/react.md +230 -0
- package/.claude/skills/redis.md +391 -0
- package/.claude/skills/refactoring.md +143 -0
- package/.claude/skills/remix.md +246 -0
- package/.claude/skills/rest-api.md +490 -0
- package/.claude/skills/rocket.md +366 -0
- package/.claude/skills/rust.md +341 -0
- package/.claude/skills/sass.md +380 -0
- package/.claude/skills/sea-orm.md +382 -0
- package/.claude/skills/security.md +167 -0
- package/.claude/skills/sequelize.md +395 -0
- package/.claude/skills/spring-boot.md +416 -0
- package/.claude/skills/sqlalchemy.md +269 -0
- package/.claude/skills/sqlx-rust.md +408 -0
- package/.claude/skills/state-jotai.md +346 -0
- package/.claude/skills/state-mobx.md +353 -0
- package/.claude/skills/state-pinia.md +431 -0
- package/.claude/skills/state-redux.md +337 -0
- package/.claude/skills/state-tanstack-query.md +434 -0
- package/.claude/skills/state-zustand.md +340 -0
- package/.claude/skills/styled-components.md +403 -0
- package/.claude/skills/svelte.md +238 -0
- package/.claude/skills/sveltekit.md +207 -0
- package/.claude/skills/symfony.md +437 -0
- package/.claude/skills/tailwind.md +279 -0
- package/.claude/skills/terraform.md +394 -0
- package/.claude/skills/testing-library.md +371 -0
- package/.claude/skills/trpc.md +426 -0
- package/.claude/skills/typeorm.md +368 -0
- package/.claude/skills/vitest.md +330 -0
- package/.claude/skills/vue.md +202 -0
- package/.claude/skills/warp.md +365 -0
- package/README.md +135 -52
- package/package.json +1 -1
- package/system/triggers.md +152 -11
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# Cypress Skill
|
|
2
|
+
|
|
3
|
+
## Test Structure
|
|
4
|
+
\`\`\`typescript
|
|
5
|
+
describe('Authentication', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
cy.visit('/login');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should login successfully', () => {
|
|
11
|
+
// Use data-cy attributes (recommended)
|
|
12
|
+
cy.get('[data-cy=email]').type('test@example.com');
|
|
13
|
+
cy.get('[data-cy=password]').type('password123');
|
|
14
|
+
cy.get('[data-cy=submit]').click();
|
|
15
|
+
|
|
16
|
+
// Assertions
|
|
17
|
+
cy.url().should('include', '/dashboard');
|
|
18
|
+
cy.get('[data-cy=welcome-message]').should('be.visible');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should show error for invalid credentials', () => {
|
|
22
|
+
cy.get('[data-cy=email]').type('wrong@example.com');
|
|
23
|
+
cy.get('[data-cy=password]').type('wrongpassword');
|
|
24
|
+
cy.get('[data-cy=submit]').click();
|
|
25
|
+
|
|
26
|
+
cy.get('[data-cy=error-message]')
|
|
27
|
+
.should('be.visible')
|
|
28
|
+
.and('contain', 'Invalid credentials');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
\`\`\`
|
|
32
|
+
|
|
33
|
+
## Selectors
|
|
34
|
+
\`\`\`typescript
|
|
35
|
+
// Recommended: data-cy attributes
|
|
36
|
+
cy.get('[data-cy=submit-button]');
|
|
37
|
+
cy.get('[data-testid=email-input]');
|
|
38
|
+
|
|
39
|
+
// By text
|
|
40
|
+
cy.contains('Submit');
|
|
41
|
+
cy.contains('button', 'Submit'); // Element type + text
|
|
42
|
+
|
|
43
|
+
// CSS selectors
|
|
44
|
+
cy.get('.btn-primary');
|
|
45
|
+
cy.get('#email');
|
|
46
|
+
cy.get('button[type="submit"]');
|
|
47
|
+
|
|
48
|
+
// Within scope
|
|
49
|
+
cy.get('[data-cy=form]').within(() => {
|
|
50
|
+
cy.get('[data-cy=email]').type('test@example.com');
|
|
51
|
+
cy.get('[data-cy=submit]').click();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Finding by index
|
|
55
|
+
cy.get('li').first();
|
|
56
|
+
cy.get('li').last();
|
|
57
|
+
cy.get('li').eq(2); // Third item (0-indexed)
|
|
58
|
+
|
|
59
|
+
// Filtering
|
|
60
|
+
cy.get('button').filter('.primary');
|
|
61
|
+
cy.get('li').not('.disabled');
|
|
62
|
+
|
|
63
|
+
// Parent/child traversal
|
|
64
|
+
cy.get('[data-cy=item]').parent();
|
|
65
|
+
cy.get('[data-cy=item]').children();
|
|
66
|
+
cy.get('[data-cy=item]').siblings();
|
|
67
|
+
cy.get('[data-cy=item]').find('button'); // Descendant
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
## Actions
|
|
71
|
+
\`\`\`typescript
|
|
72
|
+
// Click
|
|
73
|
+
cy.get('button').click();
|
|
74
|
+
cy.get('button').dblclick();
|
|
75
|
+
cy.get('button').rightclick();
|
|
76
|
+
cy.get('button').click({ force: true }); // Skip visibility check
|
|
77
|
+
cy.get('button').click({ multiple: true }); // Click all matched
|
|
78
|
+
|
|
79
|
+
// Type
|
|
80
|
+
cy.get('input').type('Hello World');
|
|
81
|
+
cy.get('input').type('Hello{enter}'); // Special keys
|
|
82
|
+
cy.get('input').type('{selectall}{backspace}'); // Clear + delete
|
|
83
|
+
cy.get('input').type('slow typing', { delay: 100 });
|
|
84
|
+
cy.get('input').clear();
|
|
85
|
+
cy.get('input').clear().type('New value');
|
|
86
|
+
|
|
87
|
+
// Special keys: {enter}, {esc}, {backspace}, {del}, {ctrl}, {shift}, {alt}
|
|
88
|
+
// Modifiers: {ctrl+a}, {shift+click}
|
|
89
|
+
|
|
90
|
+
// Select
|
|
91
|
+
cy.get('select').select('Option 1');
|
|
92
|
+
cy.get('select').select(['Option 1', 'Option 2']); // Multi-select
|
|
93
|
+
|
|
94
|
+
// Checkbox/Radio
|
|
95
|
+
cy.get('[type="checkbox"]').check();
|
|
96
|
+
cy.get('[type="checkbox"]').uncheck();
|
|
97
|
+
cy.get('[type="radio"]').check();
|
|
98
|
+
|
|
99
|
+
// Focus/Blur
|
|
100
|
+
cy.get('input').focus();
|
|
101
|
+
cy.get('input').blur();
|
|
102
|
+
|
|
103
|
+
// Scroll
|
|
104
|
+
cy.get('[data-cy=list]').scrollTo('bottom');
|
|
105
|
+
cy.get('[data-cy=item]').scrollIntoView();
|
|
106
|
+
|
|
107
|
+
// File upload
|
|
108
|
+
cy.get('input[type="file"]').selectFile('cypress/fixtures/image.png');
|
|
109
|
+
cy.get('input[type="file"]').selectFile(['file1.png', 'file2.png']);
|
|
110
|
+
|
|
111
|
+
// Drag and drop
|
|
112
|
+
cy.get('[data-cy=drag]').drag('[data-cy=drop]');
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
## Assertions
|
|
116
|
+
\`\`\`typescript
|
|
117
|
+
// Visibility
|
|
118
|
+
cy.get('button').should('be.visible');
|
|
119
|
+
cy.get('button').should('not.be.visible');
|
|
120
|
+
cy.get('button').should('exist');
|
|
121
|
+
cy.get('button').should('not.exist');
|
|
122
|
+
|
|
123
|
+
// Text content
|
|
124
|
+
cy.get('h1').should('have.text', 'Welcome');
|
|
125
|
+
cy.get('h1').should('contain', 'Welcome');
|
|
126
|
+
cy.get('h1').should('include.text', 'Welcome');
|
|
127
|
+
cy.get('p').should('not.be.empty');
|
|
128
|
+
|
|
129
|
+
// Attributes
|
|
130
|
+
cy.get('input').should('have.value', 'test@example.com');
|
|
131
|
+
cy.get('a').should('have.attr', 'href', '/home');
|
|
132
|
+
cy.get('button').should('have.class', 'btn-primary');
|
|
133
|
+
cy.get('button').should('be.disabled');
|
|
134
|
+
cy.get('input').should('be.enabled');
|
|
135
|
+
|
|
136
|
+
// State
|
|
137
|
+
cy.get('[type="checkbox"]').should('be.checked');
|
|
138
|
+
cy.get('[type="checkbox"]').should('not.be.checked');
|
|
139
|
+
cy.get('input').should('have.focus');
|
|
140
|
+
|
|
141
|
+
// Length
|
|
142
|
+
cy.get('li').should('have.length', 5);
|
|
143
|
+
cy.get('li').should('have.length.greaterThan', 3);
|
|
144
|
+
|
|
145
|
+
// CSS
|
|
146
|
+
cy.get('button').should('have.css', 'background-color', 'rgb(0, 123, 255)');
|
|
147
|
+
|
|
148
|
+
// Chaining assertions
|
|
149
|
+
cy.get('[data-cy=message]')
|
|
150
|
+
.should('be.visible')
|
|
151
|
+
.and('contain', 'Success')
|
|
152
|
+
.and('have.class', 'alert-success');
|
|
153
|
+
|
|
154
|
+
// URL assertions
|
|
155
|
+
cy.url().should('include', '/dashboard');
|
|
156
|
+
cy.url().should('eq', 'http://localhost:3000/dashboard');
|
|
157
|
+
cy.location('pathname').should('eq', '/dashboard');
|
|
158
|
+
\`\`\`
|
|
159
|
+
|
|
160
|
+
## Network Interception
|
|
161
|
+
\`\`\`typescript
|
|
162
|
+
// Intercept and mock response
|
|
163
|
+
cy.intercept('GET', '/api/users', {
|
|
164
|
+
statusCode: 200,
|
|
165
|
+
body: [{ id: 1, name: 'John' }],
|
|
166
|
+
}).as('getUsers');
|
|
167
|
+
|
|
168
|
+
// Wait for request
|
|
169
|
+
cy.visit('/users');
|
|
170
|
+
cy.wait('@getUsers');
|
|
171
|
+
cy.get('[data-cy=user]').should('have.length', 1);
|
|
172
|
+
|
|
173
|
+
// Intercept with fixture
|
|
174
|
+
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers');
|
|
175
|
+
|
|
176
|
+
// Intercept and modify response
|
|
177
|
+
cy.intercept('GET', '/api/users', (req) => {
|
|
178
|
+
req.reply((res) => {
|
|
179
|
+
res.body.push({ id: 999, name: 'Injected' });
|
|
180
|
+
res.send();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Intercept POST and verify body
|
|
185
|
+
cy.intercept('POST', '/api/users', (req) => {
|
|
186
|
+
expect(req.body).to.have.property('email', 'test@example.com');
|
|
187
|
+
req.reply({ statusCode: 201, body: { id: 1, ...req.body } });
|
|
188
|
+
}).as('createUser');
|
|
189
|
+
|
|
190
|
+
cy.get('[data-cy=submit]').click();
|
|
191
|
+
cy.wait('@createUser').its('request.body').should('include', {
|
|
192
|
+
email: 'test@example.com',
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Mock error response
|
|
196
|
+
cy.intercept('GET', '/api/users', {
|
|
197
|
+
statusCode: 500,
|
|
198
|
+
body: { error: 'Server error' },
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Delay response
|
|
202
|
+
cy.intercept('GET', '/api/users', (req) => {
|
|
203
|
+
req.reply({
|
|
204
|
+
delay: 2000,
|
|
205
|
+
body: [],
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Match with regex
|
|
210
|
+
cy.intercept({ method: 'GET', url: /\\/api\\/users\\/\\d+/ }, { fixture: 'user.json' });
|
|
211
|
+
\`\`\`
|
|
212
|
+
|
|
213
|
+
## Custom Commands
|
|
214
|
+
\`\`\`typescript
|
|
215
|
+
// cypress/support/commands.ts
|
|
216
|
+
declare global {
|
|
217
|
+
namespace Cypress {
|
|
218
|
+
interface Chainable {
|
|
219
|
+
login(email: string, password: string): Chainable<void>;
|
|
220
|
+
dataCy(value: string): Chainable<JQuery<HTMLElement>>;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Custom login command
|
|
226
|
+
Cypress.Commands.add('login', (email: string, password: string) => {
|
|
227
|
+
cy.session([email, password], () => {
|
|
228
|
+
cy.visit('/login');
|
|
229
|
+
cy.get('[data-cy=email]').type(email);
|
|
230
|
+
cy.get('[data-cy=password]').type(password);
|
|
231
|
+
cy.get('[data-cy=submit]').click();
|
|
232
|
+
cy.url().should('include', '/dashboard');
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Custom selector command
|
|
237
|
+
Cypress.Commands.add('dataCy', (value: string) => {
|
|
238
|
+
return cy.get(\`[data-cy=\${value}]\`);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Usage in tests
|
|
242
|
+
cy.login('test@example.com', 'password123');
|
|
243
|
+
cy.dataCy('submit-button').click();
|
|
244
|
+
\`\`\`
|
|
245
|
+
|
|
246
|
+
## Fixtures
|
|
247
|
+
\`\`\`typescript
|
|
248
|
+
// cypress/fixtures/users.json
|
|
249
|
+
[
|
|
250
|
+
{ "id": 1, "name": "John", "email": "john@example.com" },
|
|
251
|
+
{ "id": 2, "name": "Jane", "email": "jane@example.com" }
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
// Using fixtures
|
|
255
|
+
cy.fixture('users.json').then((users) => {
|
|
256
|
+
cy.intercept('GET', '/api/users', users);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Or directly in intercept
|
|
260
|
+
cy.intercept('GET', '/api/users', { fixture: 'users.json' });
|
|
261
|
+
|
|
262
|
+
// Type fixtures
|
|
263
|
+
cy.fixture<User[]>('users.json').then((users) => {
|
|
264
|
+
expect(users).to.have.length(2);
|
|
265
|
+
});
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
## Session & Authentication
|
|
269
|
+
\`\`\`typescript
|
|
270
|
+
// Preserve session across tests
|
|
271
|
+
beforeEach(() => {
|
|
272
|
+
cy.session('user-session', () => {
|
|
273
|
+
cy.visit('/login');
|
|
274
|
+
cy.get('[data-cy=email]').type('test@example.com');
|
|
275
|
+
cy.get('[data-cy=password]').type('password');
|
|
276
|
+
cy.get('[data-cy=submit]').click();
|
|
277
|
+
cy.url().should('include', '/dashboard');
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Programmatic login (faster)
|
|
282
|
+
Cypress.Commands.add('loginByApi', (email: string, password: string) => {
|
|
283
|
+
cy.request('POST', '/api/auth/login', { email, password }).then((response) => {
|
|
284
|
+
window.localStorage.setItem('authToken', response.body.token);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// With cookies
|
|
289
|
+
cy.setCookie('auth-token', 'my-token');
|
|
290
|
+
cy.getCookie('auth-token');
|
|
291
|
+
cy.clearCookies();
|
|
292
|
+
\`\`\`
|
|
293
|
+
|
|
294
|
+
## Configuration (cypress.config.ts)
|
|
295
|
+
\`\`\`typescript
|
|
296
|
+
import { defineConfig } from 'cypress';
|
|
297
|
+
|
|
298
|
+
export default defineConfig({
|
|
299
|
+
e2e: {
|
|
300
|
+
baseUrl: 'http://localhost:3000',
|
|
301
|
+
specPattern: 'cypress/e2e/**/*.cy.{js,ts}',
|
|
302
|
+
supportFile: 'cypress/support/e2e.ts',
|
|
303
|
+
viewportWidth: 1280,
|
|
304
|
+
viewportHeight: 720,
|
|
305
|
+
video: false,
|
|
306
|
+
screenshotOnRunFailure: true,
|
|
307
|
+
defaultCommandTimeout: 10000,
|
|
308
|
+
requestTimeout: 10000,
|
|
309
|
+
retries: {
|
|
310
|
+
runMode: 2,
|
|
311
|
+
openMode: 0,
|
|
312
|
+
},
|
|
313
|
+
env: {
|
|
314
|
+
apiUrl: 'http://localhost:3001',
|
|
315
|
+
},
|
|
316
|
+
setupNodeEvents(on, config) {
|
|
317
|
+
// Node event listeners
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
component: {
|
|
321
|
+
devServer: {
|
|
322
|
+
framework: 'react',
|
|
323
|
+
bundler: 'vite',
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
\`\`\`
|
|
328
|
+
|
|
329
|
+
## ❌ DON'T
|
|
330
|
+
- Use \`cy.wait(ms)\` for arbitrary waits
|
|
331
|
+
- Use brittle CSS selectors
|
|
332
|
+
- Test implementation details
|
|
333
|
+
- Share state between tests
|
|
334
|
+
- Use \`cy.get().then()\` when chaining works
|
|
335
|
+
|
|
336
|
+
## ✅ DO
|
|
337
|
+
- Use data-cy attributes for selectors
|
|
338
|
+
- Use cy.intercept() to mock API responses
|
|
339
|
+
- Create custom commands for reusable actions
|
|
340
|
+
- Use cy.session() for authentication
|
|
341
|
+
- Use fixtures for test data
|
|
342
|
+
- Use aliases (@) to reference intercepted requests
|
|
343
|
+
- Wait for specific network requests, not arbitrary time
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Debugging Skill
|
|
2
|
+
|
|
3
|
+
## Systematic Approach
|
|
4
|
+
\`\`\`
|
|
5
|
+
1. REPRODUCE → Can you trigger it consistently?
|
|
6
|
+
2. ISOLATE → Where exactly does it fail?
|
|
7
|
+
3. IDENTIFY → What's the root cause?
|
|
8
|
+
4. FIX → Apply minimal change
|
|
9
|
+
5. VERIFY → Test the fix + no regressions
|
|
10
|
+
\`\`\`
|
|
11
|
+
|
|
12
|
+
## Debugging Techniques
|
|
13
|
+
|
|
14
|
+
### Console Debugging
|
|
15
|
+
\`\`\`typescript
|
|
16
|
+
// ✅ Structured logging
|
|
17
|
+
console.log('[UserService.getUser]', { userId, timestamp: Date.now() });
|
|
18
|
+
|
|
19
|
+
// ✅ Console table for arrays/objects
|
|
20
|
+
console.table(users);
|
|
21
|
+
|
|
22
|
+
// ✅ Trace call stack
|
|
23
|
+
console.trace('How did we get here?');
|
|
24
|
+
|
|
25
|
+
// ✅ Group related logs
|
|
26
|
+
console.group('API Request');
|
|
27
|
+
console.log('URL:', url);
|
|
28
|
+
console.log('Payload:', payload);
|
|
29
|
+
console.groupEnd();
|
|
30
|
+
|
|
31
|
+
// ✅ Time operations
|
|
32
|
+
console.time('fetchUsers');
|
|
33
|
+
await fetchUsers();
|
|
34
|
+
console.timeEnd('fetchUsers'); // fetchUsers: 123.45ms
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
### Browser DevTools
|
|
38
|
+
\`\`\`
|
|
39
|
+
Sources Tab:
|
|
40
|
+
- Set breakpoints (click line number)
|
|
41
|
+
- Conditional breakpoints (right-click → Add conditional)
|
|
42
|
+
- Logpoints (right-click → Add logpoint) - no code changes needed
|
|
43
|
+
- Step over (F10), Step into (F11), Step out (Shift+F11)
|
|
44
|
+
|
|
45
|
+
Network Tab:
|
|
46
|
+
- Filter by XHR/Fetch for API calls
|
|
47
|
+
- Check request/response payloads
|
|
48
|
+
- Throttle network to simulate slow connections
|
|
49
|
+
- Block requests to test error handling
|
|
50
|
+
|
|
51
|
+
Performance Tab:
|
|
52
|
+
- Record and analyze runtime performance
|
|
53
|
+
- Identify long tasks and layout thrashing
|
|
54
|
+
- Memory snapshots for leak detection
|
|
55
|
+
\`\`\`
|
|
56
|
+
|
|
57
|
+
### Node.js Debugging
|
|
58
|
+
\`\`\`bash
|
|
59
|
+
# Start with inspector
|
|
60
|
+
node --inspect src/index.js
|
|
61
|
+
|
|
62
|
+
# Break on first line
|
|
63
|
+
node --inspect-brk src/index.js
|
|
64
|
+
|
|
65
|
+
# Debug in Chrome: chrome://inspect
|
|
66
|
+
\`\`\`
|
|
67
|
+
|
|
68
|
+
### VS Code Debugging
|
|
69
|
+
\`\`\`json
|
|
70
|
+
// .vscode/launch.json
|
|
71
|
+
{
|
|
72
|
+
"version": "0.2.0",
|
|
73
|
+
"configurations": [
|
|
74
|
+
{
|
|
75
|
+
"type": "node",
|
|
76
|
+
"request": "launch",
|
|
77
|
+
"name": "Debug Server",
|
|
78
|
+
"program": "\${workspaceFolder}/src/index.ts",
|
|
79
|
+
"preLaunchTask": "tsc: build",
|
|
80
|
+
"outFiles": ["\${workspaceFolder}/dist/**/*.js"]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"type": "chrome",
|
|
84
|
+
"request": "launch",
|
|
85
|
+
"name": "Debug React",
|
|
86
|
+
"url": "http://localhost:3000",
|
|
87
|
+
"webRoot": "\${workspaceFolder}/src"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
### Binary Search Method
|
|
94
|
+
\`\`\`
|
|
95
|
+
1. Add log at middle of suspect code
|
|
96
|
+
2. If bug is BEFORE log → search first half
|
|
97
|
+
3. If bug is AFTER log → search second half
|
|
98
|
+
4. Repeat until isolated
|
|
99
|
+
\`\`\`
|
|
100
|
+
|
|
101
|
+
### Memory Leak Detection
|
|
102
|
+
\`\`\`typescript
|
|
103
|
+
// Check for growing memory
|
|
104
|
+
const used = process.memoryUsage();
|
|
105
|
+
console.log('Heap:', Math.round(used.heapUsed / 1024 / 1024), 'MB');
|
|
106
|
+
|
|
107
|
+
// Browser: DevTools → Memory → Take heap snapshot
|
|
108
|
+
// Compare snapshots to find retained objects
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
### Common Bug Sources
|
|
112
|
+
- Off-by-one errors in loops
|
|
113
|
+
- Null/undefined access
|
|
114
|
+
- Async timing issues (race conditions)
|
|
115
|
+
- State mutation bugs
|
|
116
|
+
- Cache invalidation
|
|
117
|
+
- Environment differences (dev vs prod)
|
|
118
|
+
- Stale closures in React
|
|
119
|
+
- Event listener leaks
|
|
120
|
+
|
|
121
|
+
## ❌ DON'T
|
|
122
|
+
- Guess randomly and change things
|
|
123
|
+
- Ignore error messages
|
|
124
|
+
- Leave debug code in production
|
|
125
|
+
- Debug in production without feature flags
|
|
126
|
+
|
|
127
|
+
## ✅ DO
|
|
128
|
+
- Read the error message carefully
|
|
129
|
+
- Check recent changes first (git diff, git log)
|
|
130
|
+
- Use debugger breakpoints over console.log
|
|
131
|
+
- Add tests to prevent regression
|
|
132
|
+
- Use source maps for minified code
|
|
133
|
+
- Check browser console AND network tab
|