Add Mode6Test: copy of Mode5Test running on port 3000
Build & Deploy / Test & Lint (push) Has been cancelled
Build & Deploy / Security Scan (push) Has been cancelled
Code Quality / Code Quality Checks (push) Has been cancelled
Code Quality / Dependency Vulnerabilities (push) Has been cancelled
Code Quality / Performance Testing (push) Has been cancelled
Build & Deploy / Build Docker Images (push) Has been cancelled
Build & Deploy / Deploy to Kubernetes (push) Has been cancelled
Build & Deploy / Test & Lint (push) Has been cancelled
Build & Deploy / Security Scan (push) Has been cancelled
Code Quality / Code Quality Checks (push) Has been cancelled
Code Quality / Dependency Vulnerabilities (push) Has been cancelled
Code Quality / Performance Testing (push) Has been cancelled
Build & Deploy / Build Docker Images (push) Has been cancelled
Build & Deploy / Deploy to Kubernetes (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,655 @@
|
||||
# Testing Guide - Job Info Application
|
||||
|
||||
## Phase 8: End-to-End Testing
|
||||
|
||||
This guide covers comprehensive testing of the Job Info application including OAuth2 flow, token management, error handling, and offline capabilities.
|
||||
|
||||
---
|
||||
|
||||
## 1. Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
- Bun runtime installed
|
||||
- Node.js 18+ (for development)
|
||||
- Two terminals ready (one for backend, one for frontend)
|
||||
- Microsoft OAuth app registered (credentials in `.env`)
|
||||
|
||||
### Configuration Files
|
||||
```
|
||||
Mode3Test/.env - Backend OAuth credentials
|
||||
frontend/.env.local - Frontend environment variables
|
||||
```
|
||||
|
||||
### Start Services
|
||||
|
||||
**Terminal 1 - Backend (Hono + OAuth)**
|
||||
```bash
|
||||
cd /home/admin/Job-Info-Test/Mode3Test
|
||||
bun install # if needed
|
||||
bun run dev # Starts on http://localhost:3005
|
||||
```
|
||||
|
||||
**Terminal 2 - Frontend (Vite + Svelte)**
|
||||
```bash
|
||||
cd /home/admin/Job-Info-Test/frontend
|
||||
bun install # if needed
|
||||
bun run dev # Starts on http://localhost:5173
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. OAuth2 Flow Testing
|
||||
|
||||
### Test 2.1: Sign In Flow
|
||||
|
||||
**Objective**: Verify complete OAuth2 PKCE flow with Microsoft
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to http://localhost:5173/#/signin
|
||||
2. Click "Sign In with Microsoft"
|
||||
3. Verify redirect to Microsoft login
|
||||
4. Enter Microsoft credentials
|
||||
5. Grant permissions on consent screen
|
||||
6. Verify redirect back to http://localhost:5173/#/callback
|
||||
7. Verify redirect to http://localhost:5173/#/ (home)
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ User profile displayed in navigation (name, avatar)
|
||||
- ✅ Jobs list loaded and displayed
|
||||
- ✅ No console errors
|
||||
- ✅ Access token stored in sessionStorage
|
||||
- ✅ User profile stored in IndexedDB
|
||||
|
||||
**Validation Commands** (in browser console):
|
||||
```javascript
|
||||
// Check sessionStorage
|
||||
sessionStorage.getItem('access_token'); // Should return token string
|
||||
|
||||
// Check IndexedDB
|
||||
const db = await window.openDatabase();
|
||||
const user = await db.getFromStore('userProfile');
|
||||
console.log(user); // Should show user data
|
||||
|
||||
// Check store values
|
||||
window.store?.subscribe(v => console.log('Auth:', v));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test 2.2: PKCE Code Verification
|
||||
|
||||
**Objective**: Verify PKCE security flow
|
||||
|
||||
**Tools**: Browser DevTools Network tab
|
||||
|
||||
**Steps**:
|
||||
1. Open DevTools → Network tab
|
||||
2. Click "Sign In with Microsoft"
|
||||
3. Check the request to `/api/auth/authorize`
|
||||
4. Verify request body contains:
|
||||
- `code` - Authorization code from Microsoft
|
||||
- `code_verifier` - PKCE code verifier
|
||||
- `state` - CSRF token (should match state from redirect)
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Request uses POST method
|
||||
- ✅ Request includes CORS headers
|
||||
- ✅ Response contains `access_token`, `expires_in`, `user`
|
||||
- ✅ Status code 200 OK
|
||||
|
||||
---
|
||||
|
||||
## 3. Token Management Testing
|
||||
|
||||
### Test 3.1: Token Storage Security
|
||||
|
||||
**Objective**: Verify tokens stored securely
|
||||
|
||||
**Steps**:
|
||||
1. After sign in, check browser storage:
|
||||
- Open DevTools → Application → Cookies
|
||||
- Open DevTools → Application → Local Storage
|
||||
- Open DevTools → Application → Session Storage
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ `access_token` in sessionStorage (NOT localStorage)
|
||||
- ✅ No tokens in cookies
|
||||
- ✅ No tokens in localStorage
|
||||
- ✅ Token expires in ~1 hour (check `token_expiry`)
|
||||
|
||||
**Why This Matters**:
|
||||
- SessionStorage lost on browser close (prevents token theft)
|
||||
- Can't be accessed by XSS attacks in other tabs
|
||||
- Service Worker has separate access for refresh
|
||||
|
||||
---
|
||||
|
||||
### Test 3.2: Token Refresh
|
||||
|
||||
**Objective**: Verify Service Worker automatically refreshes token
|
||||
|
||||
**Steps**:
|
||||
1. Sign in successfully
|
||||
2. Wait 55+ seconds (Service Worker polls every 60 seconds)
|
||||
3. Check DevTools → Application → Service Workers
|
||||
4. Verify Service Worker status shows "activated"
|
||||
5. Monitor Console for refresh logs
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Service Worker shows "activated and running"
|
||||
- ✅ No manual action needed
|
||||
- ✅ Can continue using app without re-login
|
||||
- ✅ Token refreshes 5 minutes before expiry
|
||||
|
||||
**Debug Commands**:
|
||||
```javascript
|
||||
// Check Service Worker registration
|
||||
navigator.serviceWorker.getRegistrations().then(regs => {
|
||||
regs.forEach(reg => console.log('SW:', reg.scope, reg.active?.state));
|
||||
});
|
||||
|
||||
// Send refresh command to Service Worker
|
||||
if (navigator.serviceWorker.controller) {
|
||||
navigator.serviceWorker.controller.postMessage({
|
||||
type: 'REFRESH_TOKEN'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API Error Handling Testing
|
||||
|
||||
### Test 4.1: Network Error Handling
|
||||
|
||||
**Objective**: Verify graceful handling of network failures
|
||||
|
||||
**Steps**:
|
||||
1. Sign in successfully
|
||||
2. Open DevTools → Network tab
|
||||
3. Check "Offline" checkbox (simulates no network)
|
||||
4. Try to perform action (search, load jobs)
|
||||
5. Verify error message displayed
|
||||
6. Uncheck "Offline"
|
||||
7. Verify app recovers automatically
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Error message shown: "Network error - please check your connection"
|
||||
- ✅ Retry button appears
|
||||
- ✅ App recovers when network restored
|
||||
- ✅ No unhandled exceptions in console
|
||||
- ✅ Error logged to structured logger
|
||||
|
||||
---
|
||||
|
||||
### Test 4.2: API Error Responses
|
||||
|
||||
**Objective**: Verify handling of HTTP error responses
|
||||
|
||||
**Steps**:
|
||||
1. Use DevTools → Network → Throttle to simulate slow network
|
||||
2. Open DevTools → Network → Response headers
|
||||
3. Modify response (requires proxy like Charles/Fiddler):
|
||||
- Change 200 to 401 (Unauthorized)
|
||||
- Change 200 to 403 (Forbidden)
|
||||
- Change 200 to 500 (Server Error)
|
||||
|
||||
**Expected Results** (per error type):
|
||||
|
||||
**401 Unauthorized**:
|
||||
- ✅ "Session expired" message shown
|
||||
- ✅ Redirect to sign in page
|
||||
- ✅ Error logged with context
|
||||
|
||||
**403 Forbidden**:
|
||||
- ✅ "You don't have permission" message shown
|
||||
- ✅ Retry option disabled
|
||||
- ✅ Contact admin message shown
|
||||
|
||||
**500 Server Error**:
|
||||
- ✅ "Server error" message shown
|
||||
- ✅ Retry button enabled
|
||||
- ✅ Auto-retry happens 2-3 times
|
||||
|
||||
---
|
||||
|
||||
### Test 4.3: Request Timeout
|
||||
|
||||
**Objective**: Verify timeout handling
|
||||
|
||||
**Steps**:
|
||||
1. Use DevTools → Network → Throttle to "Slow 3G"
|
||||
2. Trigger API call (load jobs)
|
||||
3. Wait for 30+ seconds
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Request times out after 30 seconds
|
||||
- ✅ Error message: "Request timeout - please try again"
|
||||
- ✅ Retry automatically attempted
|
||||
- ✅ No hung requests in Network tab
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Storage Testing
|
||||
|
||||
### Test 5.1: IndexedDB Jobs Cache
|
||||
|
||||
**Objective**: Verify jobs cached locally
|
||||
|
||||
**Steps**:
|
||||
1. Sign in and load jobs
|
||||
2. Open DevTools → Application → IndexedDB → job-info-db
|
||||
3. Check `jobs` object store
|
||||
4. Sign out
|
||||
5. Go back to sign in page
|
||||
6. Turn off network ("Offline" in DevTools)
|
||||
7. Sign in again (offline)
|
||||
8. Check if previous jobs available
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Jobs loaded from IndexedDB when offline
|
||||
- ✅ Jobs display without network
|
||||
- ✅ Database shows ~50-100 jobs
|
||||
- ✅ Each job has: id, title, description, location, salary
|
||||
|
||||
---
|
||||
|
||||
### Test 5.2: Cache Invalidation
|
||||
|
||||
**Objective**: Verify old data cleaned up
|
||||
|
||||
**Steps**:
|
||||
1. Sign in and load jobs
|
||||
2. Note timestamp in IndexedDB
|
||||
3. Wait 1 hour (or modify cache expiry in constants)
|
||||
4. Refresh page
|
||||
5. Check if cache refreshed from API
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Fresh jobs fetched from API
|
||||
- ✅ Cache timestamp updated
|
||||
- ✅ Stale data not used
|
||||
- ✅ No cache inconsistencies
|
||||
|
||||
---
|
||||
|
||||
## 6. UI Component Testing
|
||||
|
||||
### Test 6.1: Responsive Design
|
||||
|
||||
**Objectives**: Verify layout works on all screen sizes
|
||||
|
||||
**Steps**:
|
||||
1. Open DevTools → Responsive Design Mode
|
||||
2. Test on:
|
||||
- Mobile (375px - iPhone SE)
|
||||
- Tablet (768px - iPad)
|
||||
- Desktop (1920px - 4K)
|
||||
3. Verify:
|
||||
- Navigation stays accessible
|
||||
- Job list responsive
|
||||
- Search bar functional
|
||||
- No horizontal scroll
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ All sizes readable and usable
|
||||
- ✅ TailwindCSS classes applied correctly
|
||||
- ✅ Touch targets ≥ 44x44 pixels on mobile
|
||||
- ✅ No overflow or layout shifts
|
||||
|
||||
---
|
||||
|
||||
### Test 6.2: Error Boundary Display
|
||||
|
||||
**Objective**: Verify error UI works
|
||||
|
||||
**Steps**:
|
||||
1. Sign in
|
||||
2. Open DevTools → Console
|
||||
3. Manually throw error in component:
|
||||
```javascript
|
||||
throw new Error('Test error for ErrorBoundary');
|
||||
```
|
||||
4. Verify error displayed gracefully
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Error Boundary component displays
|
||||
- ✅ User-friendly message shown
|
||||
- ✅ Development details (stack) available
|
||||
- ✅ Recovery suggestions shown
|
||||
- ✅ Retry button works
|
||||
|
||||
---
|
||||
|
||||
## 7. Logging & Debugging
|
||||
|
||||
### Test 7.1: Structured Logging
|
||||
|
||||
**Objective**: Verify comprehensive logging
|
||||
|
||||
**Steps**:
|
||||
1. Sign in
|
||||
2. Load jobs
|
||||
3. Perform search
|
||||
4. Open DevTools → Console
|
||||
5. Check for log messages with format:
|
||||
```
|
||||
[module] message {context}
|
||||
```
|
||||
|
||||
**Expected Log Entries**:
|
||||
- `[OAuth] Token refreshed successfully`
|
||||
- `[API] GET /api/graph/me - 200`
|
||||
- `[Jobs] Loaded X jobs from cache`
|
||||
- `[Search] Searching for: term`
|
||||
|
||||
---
|
||||
|
||||
### Test 7.2: Log Export
|
||||
|
||||
**Objective**: Verify log export for debugging
|
||||
|
||||
**Steps**:
|
||||
1. Sign in and perform actions
|
||||
2. In Console, run:
|
||||
```javascript
|
||||
window.logger.exportLogs();
|
||||
```
|
||||
3. Check Downloads folder
|
||||
4. Verify JSON file contains:
|
||||
- Timestamps
|
||||
- Log levels
|
||||
- Module names
|
||||
- Context data
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ JSON file downloads
|
||||
- ✅ Contains 100+ log entries
|
||||
- ✅ Properly formatted
|
||||
- ✅ Includes error context
|
||||
|
||||
---
|
||||
|
||||
## 8. Performance Testing
|
||||
|
||||
### Test 8.1: Build Size
|
||||
|
||||
**Objective**: Verify production build is optimized
|
||||
|
||||
**Command**:
|
||||
```bash
|
||||
cd /home/admin/Job-Info-Test/frontend
|
||||
bun run build
|
||||
```
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Total JS: < 50 kB (gzip)
|
||||
- ✅ CSS: < 5 kB (gzip)
|
||||
- ✅ HTML: < 1 kB
|
||||
- ✅ Build time: < 5 seconds
|
||||
|
||||
**Current Metrics**:
|
||||
```
|
||||
JS: 42.56 kB (gzip)
|
||||
CSS: 3.96 kB (gzip)
|
||||
HTML: 0.32 kB
|
||||
Build: 2.68s
|
||||
Modules: 51
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test 8.2: Load Time
|
||||
|
||||
**Objective**: Verify fast initial load
|
||||
|
||||
**Steps**:
|
||||
1. Clear browser cache
|
||||
2. Open DevTools → Network tab
|
||||
3. Navigate to http://localhost:5173
|
||||
4. Check timing breakdown:
|
||||
- Time to First Byte (TTFB)
|
||||
- First Contentful Paint (FCP)
|
||||
- Largest Contentful Paint (LCP)
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ TTFB: < 100ms
|
||||
- ✅ FCP: < 1s
|
||||
- ✅ LCP: < 2.5s (Core Web Vitals target)
|
||||
- ✅ Total load: < 3s
|
||||
|
||||
---
|
||||
|
||||
## 9. Security Testing
|
||||
|
||||
### Test 9.1: XSS Protection
|
||||
|
||||
**Objective**: Verify no XSS vulnerabilities
|
||||
|
||||
**Steps**:
|
||||
1. Try to inject script in search:
|
||||
```
|
||||
<script>alert('XSS')</script>
|
||||
```
|
||||
2. Check if alert fires (it shouldn't)
|
||||
3. Check rendered HTML
|
||||
4. Verify script treated as text
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ No alert shown
|
||||
- ✅ Input rendered as plain text
|
||||
- ✅ Script tags escaped
|
||||
- ✅ Content Security Policy working
|
||||
|
||||
---
|
||||
|
||||
### Test 9.2: CSRF Protection
|
||||
|
||||
**Objective**: Verify CSRF token handling
|
||||
|
||||
**Steps**:
|
||||
1. In Network tab, check requests to backend
|
||||
2. Verify state parameter in OAuth flow
|
||||
3. Check CORS headers are set correctly
|
||||
4. Try to call API from different origin (if possible)
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ State token matches request/response
|
||||
- ✅ CORS prevents cross-origin requests
|
||||
- ✅ SameSite cookie policy enforced
|
||||
- ✅ No state token reuse
|
||||
|
||||
---
|
||||
|
||||
## 10. Accessibility Testing
|
||||
|
||||
### Test 10.1: Keyboard Navigation
|
||||
|
||||
**Steps**:
|
||||
1. Sign in to app
|
||||
2. Press Tab repeatedly
|
||||
3. Navigate through:
|
||||
- Navigation menu
|
||||
- Search input
|
||||
- Job list items
|
||||
- Buttons
|
||||
4. Verify focus visible on all elements
|
||||
5. Press Enter on buttons to activate
|
||||
6. Verify Esc closes modals
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ All interactive elements reachable via keyboard
|
||||
- ✅ Focus indicator clearly visible
|
||||
- ✅ Tab order logical
|
||||
- ✅ Enter activates buttons
|
||||
- ✅ Esc closes overlays
|
||||
|
||||
---
|
||||
|
||||
### Test 10.2: Screen Reader
|
||||
|
||||
**Steps**:
|
||||
1. Enable screen reader:
|
||||
- macOS: Cmd+F5 (VoiceOver)
|
||||
- Windows: Windows+Enter (Narrator)
|
||||
- Linux: NVDA (free)
|
||||
2. Navigate through app
|
||||
3. Check that:
|
||||
- Page title announced
|
||||
- Headings identified
|
||||
- Links have labels
|
||||
- Form inputs have labels
|
||||
- Errors announced
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Navigation menu announced correctly
|
||||
- ✅ Page sections identified
|
||||
- ✅ Buttons have accessible labels
|
||||
- ✅ Form inputs labeled
|
||||
- ✅ Errors announced to screen reader
|
||||
|
||||
---
|
||||
|
||||
## 11. Regression Testing Checklist
|
||||
|
||||
Use this checklist before each release:
|
||||
|
||||
### Authentication
|
||||
- [ ] Sign in flow completes successfully
|
||||
- [ ] Token stored securely in sessionStorage
|
||||
- [ ] User profile displayed in navigation
|
||||
- [ ] Sign out clears auth state
|
||||
- [ ] Protected pages redirect to sign in when logged out
|
||||
|
||||
### Data Loading
|
||||
- [ ] Jobs list loads on home page
|
||||
- [ ] Search filters jobs correctly
|
||||
- [ ] Pagination works (if implemented)
|
||||
- [ ] Job details display when clicked
|
||||
- [ ] Loading state shows spinner
|
||||
|
||||
### Error Handling
|
||||
- [ ] Network errors show user message
|
||||
- [ ] API errors handled gracefully
|
||||
- [ ] No unhandled exceptions in console
|
||||
- [ ] Error recovery buttons work
|
||||
- [ ] Errors logged with context
|
||||
|
||||
### Token Refresh
|
||||
- [ ] Service Worker registers successfully
|
||||
- [ ] Token refreshes before expiry
|
||||
- [ ] App continues working after refresh
|
||||
- [ ] No authentication prompts during normal use
|
||||
|
||||
### UI/UX
|
||||
- [ ] Navigation responsive on all sizes
|
||||
- [ ] Colors have sufficient contrast
|
||||
- [ ] Font sizes readable on mobile
|
||||
- [ ] Buttons easily tappable (44x44px min)
|
||||
- [ ] Forms accessible via keyboard
|
||||
|
||||
### Performance
|
||||
- [ ] Build size within limits
|
||||
- [ ] No console errors or warnings
|
||||
- [ ] Page loads in < 3 seconds
|
||||
- [ ] Interactions respond immediately
|
||||
- [ ] No memory leaks (check DevTools)
|
||||
|
||||
---
|
||||
|
||||
## 12. Known Limitations & Workarounds
|
||||
|
||||
### Issue 1: Token Expiry During Long Session
|
||||
**Symptom**: Suddenly get 401 errors after 1 hour
|
||||
**Cause**: Token expires, Service Worker refresh fails
|
||||
**Workaround**: Service Worker auto-refreshes at 55 min mark (before 1hr expiry)
|
||||
**Fix**: Already implemented in Service Worker
|
||||
|
||||
### Issue 2: IndexedDB Not Available
|
||||
**Symptom**: Jobs don't persist offline
|
||||
**Cause**: Incognito/Private mode disables IndexedDB
|
||||
**Workaround**: Use normal browser mode
|
||||
**Fix**: Graceful fallback to in-memory cache (already implemented)
|
||||
|
||||
### Issue 3: CORS Errors
|
||||
**Symptom**: "CORS policy: No 'Access-Control-Allow-Origin'"
|
||||
**Cause**: API doesn't allow requests from localhost:5173
|
||||
**Fix**: Ensure vite.config.ts has correct proxy:
|
||||
```typescript
|
||||
proxy: {
|
||||
'/api': 'http://localhost:3005'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Testing Automation
|
||||
|
||||
### Unit Test Example (Optional - Future)
|
||||
```typescript
|
||||
import { describe, it, expect } from 'bun:test';
|
||||
import { classifyError } from '../services/errorHandler';
|
||||
|
||||
describe('errorHandler', () => {
|
||||
it('should classify network error', () => {
|
||||
const error = new Error('Network error');
|
||||
const classified = classifyError(error);
|
||||
expect(classified.type).toBe('network');
|
||||
});
|
||||
|
||||
it('should classify auth error', () => {
|
||||
const error = new Error('401 Unauthorized');
|
||||
const classified = classifyError(error);
|
||||
expect(classified.type).toBe('auth');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. Test Results Template
|
||||
|
||||
**Date**: ___________
|
||||
**Tester**: ___________
|
||||
**Environment**:
|
||||
- Browser: ___________
|
||||
- OS: ___________
|
||||
- Build: ___________
|
||||
|
||||
**Test Results**:
|
||||
- [ ] OAuth2 Flow: PASS / FAIL
|
||||
- [ ] Token Management: PASS / FAIL
|
||||
- [ ] Error Handling: PASS / FAIL
|
||||
- [ ] Data Storage: PASS / FAIL
|
||||
- [ ] UI Components: PASS / FAIL
|
||||
- [ ] Performance: PASS / FAIL
|
||||
- [ ] Accessibility: PASS / FAIL
|
||||
|
||||
**Issues Found**:
|
||||
1. ___________
|
||||
2. ___________
|
||||
3. ___________
|
||||
|
||||
**Notes**:
|
||||
___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This testing guide ensures comprehensive validation of:
|
||||
✅ OAuth2 security flow
|
||||
✅ Token management and refresh
|
||||
✅ Error handling and recovery
|
||||
✅ Data persistence
|
||||
✅ UI/UX across devices
|
||||
✅ Performance benchmarks
|
||||
✅ Accessibility compliance
|
||||
✅ Security best practices
|
||||
|
||||
**Next Steps**:
|
||||
1. Execute tests in order (sections 2-7)
|
||||
2. Document any failures
|
||||
3. Fix issues using logger insights
|
||||
4. Re-run failed tests
|
||||
5. Move to deployment when all pass
|
||||
Reference in New Issue
Block a user