The CSS & JS Minifier extension uses a comprehensive testing approach with VS Code's extension testing framework and Mocha. The test suite includes 29 tests covering all functionality with robust rate limiting and file management strategies.
-
Main Functionality Tests (21 tests)
- Basic CSS/JS minification (in-place and new file)
- File prefix configurations (
.min,-min,.compressed, etc.) - Explorer context menu actions
- Empty file and unsupported file type validation
-
CSS nth-child Test Suite (2 tests)
- CSS nth-child selectors minification (in-place)
- CSS nth-child selectors with new file creation
- Validates URL encoding fixes for
+characters
-
Keybinding Test Suite (2 tests)
- Keyboard shortcut functionality
- Command palette integration
-
Configuration Test Suite (4 tests)
autoOpenNewFilesetting validation (enabled/disabled)minifiedNewFilePrefixcustom prefix configurationminifyInNewFilevs in-place minification behavior- Complex configuration state management
- Rate Limit: 30 requests per minute
- Impact: Test suites with 29 tests trigger rate limiting without delays
- Symptoms: Tests fail with 429 HTTP status code or timeout errors
/**
* Rate limiting configuration for Toptal API tests
*/
const RATE_LIMIT_CONFIG = {
// Delay between tests in milliseconds (3 seconds)
TEST_DELAY_MS: 3000,
// Maximum retries for failed requests
MAX_RETRIES: 3,
// Timeout for individual tests (5 seconds)
TEST_TIMEOUT_MS: 5000
};
/**
* Adds delay between tests to respect Toptal API rate limits
*/
async function delayBetweenTests(ms: number = RATE_LIMIT_CONFIG.TEST_DELAY_MS): Promise<void> {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}// Configuration tests require additional delays for VS Code setting updates
await config.update("autoOpenNewFile", true, true);
await delayBetweenTests(1000); // Wait for configuration to take effect
// File creation operations need processing time
await vscode.commands.executeCommand("extension.minifyInNewFile");
await delayBetweenTests(1000); // Wait for file creation// All test suites use extended timeouts
suite("Test Suite Name", function () {
this.timeout(RATE_LIMIT_CONFIG.TEST_TIMEOUT_MS); // 5 seconds per test
this.afterEach(async function () {
await delayBetweenTests(); // 3 seconds between tests
});
});Configuration tests use temporary files to avoid modifying source fixtures:
// Copy source file to avoid modification issues
const sourceFile = path.join(__dirname, "..", "..", "src", "test", "fixtures", "test.css");
const testFile = path.join(__dirname, "fixtures", "temp-test.css");
fs.copyFileSync(sourceFile, testFile);
// Use temporary file for testing
const cssUri = vscode.Uri.file(testFile);
const cssDocument = await vscode.workspace.openTextDocument(cssUri);
// Clean up both generated and temporary files
if (fs.existsSync(newFileUri.fsPath)) {
fs.unlinkSync(newFileUri.fsPath);
}
if (fs.existsSync(testFile)) {
fs.unlinkSync(testFile);
}Configuration test suite includes sophisticated cleanup:
this.beforeAll(async function () {
// Reset all configurations to defaults first
const config = vscode.workspace.getConfiguration("css-js-minifier");
await config.update("minifyInNewFile", true, true);
await config.update("autoOpenNewFile", true, true);
await config.update("minifiedNewFilePrefix", ".min", true);
// Clean up any generated minified files
const fixturesDir = path.join(__dirname, "fixtures");
if (fs.existsSync(fixturesDir)) {
const files = fs.readdirSync(fixturesDir);
for (const file of files) {
if (file.includes("min") || file.includes("compressed")) {
const filePath = path.join(fixturesDir, file);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
}
}
}
// Restore original fixture files from source
const fixturesToRestore = ["test.css", "test.js", "nth-child-test.css"];
for (const fixture of fixturesToRestore) {
const srcPath = path.join(srcFixturesDir, fixture);
const outPath = path.join(outFixturesDir, fixture);
if (fs.existsSync(srcPath)) {
const originalContent = fs.readFileSync(srcPath, 'utf8');
fs.writeFileSync(outPath, originalContent);
}
}
});// Clean up sinon spies after each test to prevent conflicts
this.afterEach(function () {
sinon.restore();
});The project includes optimized VS Code tasks for efficient test execution. Access via Ctrl/Cmd + Shift + P → "Tasks: Run Task":
- "Test: Run All Tests" - Complete test suite (29 tests) with full compilation and linting
- "Test: Compile and Build Only" - Prepare tests without running (TypeScript + webpack + lint + fixtures)
- "Test: Configuration Suite Only" - Run only Configuration Test Suite (4 tests)
- "Test: CSS nth-child Suite Only" - Run only CSS nth-child Test Suite (2 tests)
- "Test: Keybinding Suite Only" - Run only Keybinding Test Suite (2 tests)
- "Test: Main Functionality Suite Only" - Run only Main Functionality Test Suite (21 tests)
- "Test: Specific Test by Name" - Run specific test with prompt input (e.g., "autoOpenNewFile")
- "Test: Quick Compile and Test" - Fast TypeScript compilation without webpack/linting
- "npm: watch" - Webpack watch mode for development
- "npm: watch-tests" - TypeScript watch mode for test files
- "tasks: watch-tests" - Combined build and test watching
# Complete test suite
npm test
# Individual suite execution
npx vscode-test --grep "Configuration Test Suite"
npx vscode-test --grep "CSS nth-child Test Suite"
npx vscode-test --grep "Keybinding Test Suite"
# Specific test execution
npx vscode-test --grep "autoOpenNewFile setting - enabled"
# Build only
npm run pretest
npm run compile-tests
npm run copy-fixturesFor Feature Development:
- Start watch mode:
Ctrl/Cmd + Shift + P→ "Tasks: Run Task" → "tasks: watch-tests" - Run specific tests: "Test: Configuration Suite Only" (for configuration features)
- Run full suite before commit: "Test: Run All Tests"
For Bug Fixing:
- Identify failing suite: "Test: Run All Tests"
- Focus on specific suite: "Test: CSS nth-child Suite Only" (for CSS issues)
- Target specific test: "Test: Specific Test by Name" → enter test name
For CI/CD Validation:
- Full build verification: "Test: Compile and Build Only"
- Complete test validation: "Test: Run All Tests"
- GitHub Actions runs tests on multiple OS (macOS, Ubuntu, Windows)
- Each OS instance makes independent API requests
- Extended timeouts (10 minutes) to handle rate limiting
- Automatic retry logic implemented for network failures
src/test/
├── extension.test.ts # Main test suite (29 tests)
├── fixtures/ # Test data files
│ ├── test.css # Basic CSS test file (22 chars)
│ ├── test.js # Basic JS test file (176 chars)
│ ├── nth-child-test.css # CSS nth-child test case (477 chars)
│ ├── empty.css # Empty file test case
│ ├── empty.js # Empty file test case
│ └── test.txt # Unsupported file type
└── (auto-generated during pretest)
└── out/test/fixtures/ # Copied fixtures with temporary files
Special handling for + characters in CSS selectors:
// Manual encoding to preserve JavaScript syntax
const manuallyEncoded = 'input=' + encodeURIComponent(text);
// Prevents URLSearchParams from corrupting + characters
// OLD: URLSearchParams would convert "2n + 1" to "2n 1"
// NEW: Manual encoding preserves "2n + 1" correctly- CSS:
https://www.toptal.com/developers/cssminifier/api/raw - JavaScript:
https://www.toptal.com/developers/javascript-minifier/api/raw - Content-Type:
application/x-www-form-urlencoded - Method: POST with form-encoded data
// CSS minification expectations
const cssMinifiedContent = "p{color:red}";
// JavaScript minification expectations
const jsMinifiedContent = 'function test(){for(var r="Hello, World!",o="",e=0;e<r.length;e++)o+=String.fromCharCode(r.charCodeAt(e)+1);return o}';
// CSS nth-child expected result
const expectedMinified = ".container:nth-child(odd){background-color:#fff;margin:10px}div:nth-child(odd){color:#00f;padding:5px}.item:nth-child(3nof.special){font-weight:700;border:1px solid red}p:nth-child(2n){text-align:center;font-size:14px}.menu-item:nth-child(2n+1of.active){display:block;opacity:.8}";- Original Location:
src/test/fixtures/(version controlled) - Test Location:
out/test/fixtures/(copied during pretest) - Temporary Files: Created with unique names to avoid conflicts
- Cleanup: Automatic removal of generated and temporary files
-
Rate Limiting (429 Error)
- Symptom: Tests pass individually but fail in suite
- Solution: Increase
TEST_DELAY_MSto 4000ms or add individual test delays - Detection: Tests return original content unchanged
-
Configuration Test Failures
- Symptom:
autoOpenNewFiletests fail with "file not created" - Solution: Use temporary files and increase file creation delays
- Root Cause: VS Code configuration updates are asynchronous
- Symptom:
-
File Path Issues
- Symptom: Fixture files not found
- Solution: Ensure
npm run copy-fixturesexecuted properly - Debug: Check
out/test/fixtures/directory exists
-
In-Place Modification Conflicts
- Symptom: Tests interfere with each other
- Solution: Use unique temporary file names per test
- Prevention: Comprehensive cleanup in
beforeAllhooks
- API Direct Testing
# Test CSS minification directly
curl -X POST https://www.toptal.com/developers/cssminifier/api/raw \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "input=.container:nth-child(2n + 1) { color: red; }"- Individual Test Execution
# Run specific test by name
npx vscode-test --grep "autoOpenNewFile setting - enabled"
# Run only configuration tests
npx vscode-test --grep "Configuration Test Suite"- Enhanced Debug Logging
// Add temporary debugging in tests
console.log('Original content length:', originalContent.length);
console.log('Minified content length:', minifiedContent.length);
console.log('File exists:', fs.existsSync(newFileUri.fsPath));- Full Suite: ~2 minutes locally (with 3s delays)
- CI Environment: ~5-8 minutes (includes setup + rate limiting)
- Configuration Tests: ~20 seconds (4 tests with file operations)
- nth-child Tests: ~10 seconds (2 tests with API calls)
- Extension Host: Automatically managed by VS Code
- Fixture Files: Total ~1KB across all test files
- Temporary Files: Created/cleaned per test, minimal footprint
- API Responses: Small payloads, no memory concerns
- Parallel Execution: Not recommended due to API rate limits
- Test Ordering: Group API-intensive tests with longer delays
- Fixture Caching: Source files remain unchanged, copied as needed
- Cleanup Batching: Use
beforeAllfor bulk operations
- Descriptive Names: Remove "Issue #" prefixes, use clear functionality descriptions
- Setup/Cleanup: Include comprehensive cleanup in each test suite
- Async Handling: Always await API calls and file operations
- Error Messages: Provide detailed assertions with file paths and expected values
- Rate Limit Respect: Minimum 3-second delays between API calls
- Realistic Data: Test with actual CSS/JS that users would minify
- Error Handling: Validate both success and failure scenarios
- Content Verification: Assert specific minified output, not just "changed"
- Extended Timeouts: 10-minute workflow timeouts for rate limiting
- Environment Variables: No secrets required for Toptal API
- Artifact Collection: Test results and coverage reports
- Retry Logic: Built into test framework, not needed externally
- Source Protection: Never modify files in
src/test/fixtures/ - Temporary Naming: Use unique prefixes (
temp-test.css,temp-custom-test.css) - Complete Cleanup: Remove both generated and temporary files
- Fixture Restoration: Copy fresh content from source when needed
The test suite evolved through several iterations to achieve 100% reliability:
- Initial: Basic API tests without rate limiting (frequent failures)
- v2: Added delays but insufficient for configuration tests
- v3: Implemented temporary file strategy for test isolation
- v4: Comprehensive cleanup and configuration management
- Current: 29/29 tests passing consistently with robust error handling
- Configuration Tests: VS Code settings updates require explicit delays
- File Creation: Asynchronous operations need additional wait time
- Test Isolation: In-place modifications require temporary file strategy
- API Encoding: Manual
encodeURIComponentneeded for CSS+characters - Spy Management: Sinon spies must be restored to prevent test interference
Last Updated: October 16, 2025
Extension Version: 1.0.0
Test Suite: 29 tests, 100% passing rate