From 320458288536fe95b3e5b93a80f6f19ca2040a51 Mon Sep 17 00:00:00 2001 From: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com> Date: Sat, 12 Jul 2025 00:38:49 +0300 Subject: [PATCH] chore: fix use-tag e2e test --- jest.e2e.config.js | 60 ++++--- package-lock.json | 47 ----- package.json | 1 - tests/e2e/setup/global-setup.js | 30 +++- tests/e2e/setup/global-teardown.js | 7 +- tests/e2e/tests/commands/use-tag.test.js | 210 +++++++++++++---------- tests/e2e/utils/logger.js | 5 +- 7 files changed, 187 insertions(+), 173 deletions(-) diff --git a/jest.e2e.config.js b/jest.e2e.config.js index ba62d2ea..1461bfe3 100644 --- a/jest.e2e.config.js +++ b/jest.e2e.config.js @@ -20,6 +20,8 @@ export default { testTimeout: 180000, // 3 minutes default (AI operations can be slow) maxWorkers: 1, // Run E2E tests sequentially to avoid conflicts verbose: true, + // Suppress console output for cleaner test results + silent: false, setupFilesAfterEnv: ['/tests/e2e/setup/jest-setup.js'], globalSetup: '/tests/e2e/setup/global-setup.js', globalTeardown: '/tests/e2e/setup/global-teardown.js', @@ -30,38 +32,40 @@ export default { ], coverageDirectory: '/coverage-e2e', // Custom reporters for better E2E test output + // Transform configuration to match unit tests + transform: {}, + transformIgnorePatterns: ['/node_modules/'], + // Module configuration + moduleNameMapper: { + '^@/(.*)$': '/$1' + }, + moduleDirectories: ['node_modules', ''], + // Reporters configuration reporters: [ 'default', [ - 'jest-stare', + 'jest-html-reporters', { - resultDir: 'test-results', - reportTitle: 'Task Master E2E Test Report', - additionalResultsProcessors: ['jest-junit'], - coverageLink: '../coverage-e2e/lcov-report/index.html', - jestStareConfigJson: 'jest-stare.config.json', - jestGlobalConfigJson: 'jest.e2e.config.json', - report: true, - reportSummary: true, - reportHeadline: 'Task Master E2E Test Results', - reportDescriptionHeadline: 'Comprehensive E2E test suite for all CLI commands', - disableCharts: false, - resultJson: 'jest-results.json', - resultHtml: 'index.html' - } - ], - [ - 'jest-junit', - { - outputDirectory: '/test-results', - outputName: 'e2e-junit.xml', - classNameTemplate: '{classname} - {title}', - titleTemplate: '{classname} - {title}', - ancestorSeparator: ' โ€บ ', - suiteNameTemplate: '{filepath}', - includeConsoleOutput: true, - includeShortConsoleOutput: true, - reportTestSuiteErrors: true + publicPath: './test-results', + filename: 'index.html', + pageTitle: 'Task Master E2E Test Report', + expand: true, + openReport: false, + hideIcon: false, + includeFailureMsg: true, + enableMergeData: true, + dataMergeLevel: 1, + inlineSource: false, + customInfos: [ + { + title: 'Environment', + value: 'E2E Testing' + }, + { + title: 'Test Type', + value: 'CLI Commands' + } + ] } ] ], diff --git a/package-lock.json b/package-lock.json index 9d0cd266..5d67117e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,7 +70,6 @@ "jest": "^29.7.0", "jest-environment-node": "^29.7.0", "jest-html-reporters": "^3.1.7", - "jest-junit": "^16.0.0", "mcp-jest": "^1.0.10", "mock-fs": "^5.5.0", "prettier": "^3.5.3", @@ -9806,32 +9805,6 @@ "node": ">= 10.0.0" } }, - "node_modules/jest-junit": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz", - "integrity": "sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "mkdirp": "^1.0.4", - "strip-ansi": "^6.0.1", - "uuid": "^8.3.2", - "xml": "^1.0.1" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/jest-junit/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/jest-leak-detector": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", @@ -11131,19 +11104,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/mock-fs": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz", @@ -13791,13 +13751,6 @@ } } }, - "node_modules/xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", - "dev": true, - "license": "MIT" - }, "node_modules/xsschema": { "version": "0.3.0-beta.8", "resolved": "https://registry.npmjs.org/xsschema/-/xsschema-0.3.0-beta.8.tgz", diff --git a/package.json b/package.json index 6bf1697f..8b9d4dec 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,6 @@ "ink": "^5.0.1", "jest": "^29.7.0", "jest-environment-node": "^29.7.0", - "jest-junit": "^16.0.0", "jest-html-reporters": "^3.1.7", "mcp-jest": "^1.0.10", "mock-fs": "^5.5.0", diff --git a/tests/e2e/setup/global-setup.js b/tests/e2e/setup/global-setup.js index 51d16345..8c0c7d29 100644 --- a/tests/e2e/setup/global-setup.js +++ b/tests/e2e/setup/global-setup.js @@ -3,17 +3,27 @@ * Runs once before all test suites */ -const { execSync } = require('child_process'); -const { existsSync } = require('fs'); -const { join } = require('path'); +import { execSync } from 'child_process'; +import { existsSync } from 'fs'; +import { join } from 'path'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; -module.exports = async () => { - console.log('\n๐Ÿš€ Setting up E2E test environment...\n'); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +export default async () => { + // Silent mode for cleaner output + if (!process.env.JEST_SILENT_REPORTER) { + console.log('\n๐Ÿš€ Setting up E2E test environment...\n'); + } try { // Ensure task-master is linked globally const projectRoot = join(__dirname, '../../..'); - console.log('๐Ÿ“ฆ Linking task-master globally...'); + if (!process.env.JEST_SILENT_REPORTER) { + console.log('๐Ÿ“ฆ Linking task-master globally...'); + } execSync('npm link', { cwd: projectRoot, stdio: 'inherit' @@ -26,13 +36,17 @@ module.exports = async () => { 'โš ๏ธ Warning: .env file not found. Some tests may fail without API keys.' ); } else { - console.log('โœ… .env file found'); + if (!process.env.JEST_SILENT_REPORTER) { + console.log('โœ… .env file found'); + } } // Verify task-master command is available try { execSync('task-master --version', { stdio: 'pipe' }); - console.log('โœ… task-master command is available\n'); + if (!process.env.JEST_SILENT_REPORTER) { + console.log('โœ… task-master command is available\n'); + } } catch (error) { throw new Error( 'task-master command not found. Please ensure npm link succeeded.' diff --git a/tests/e2e/setup/global-teardown.js b/tests/e2e/setup/global-teardown.js index 3c49338c..3c49417b 100644 --- a/tests/e2e/setup/global-teardown.js +++ b/tests/e2e/setup/global-teardown.js @@ -3,8 +3,11 @@ * Runs once after all test suites */ -module.exports = async () => { - console.log('\n๐Ÿงน Cleaning up E2E test environment...\n'); +export default async () => { + // Silent mode for cleaner output + if (!process.env.JEST_SILENT_REPORTER) { + console.log('\n๐Ÿงน Cleaning up E2E test environment...\n'); + } // Any global cleanup needed // Note: Individual test directories are cleaned up in afterEach hooks diff --git a/tests/e2e/tests/commands/use-tag.test.js b/tests/e2e/tests/commands/use-tag.test.js index a8963a53..f0c50a9c 100644 --- a/tests/e2e/tests/commands/use-tag.test.js +++ b/tests/e2e/tests/commands/use-tag.test.js @@ -1,131 +1,169 @@ -const path = require('path'); -const fs = require('fs'); -const { - setupTestEnvironment, - cleanupTestEnvironment, - runCommand -} = require('../../helpers/testHelpers'); +import { describe, it, expect, beforeEach, afterEach } from '@jest/globals'; +import { mkdtempSync, existsSync, readFileSync, rmSync, writeFileSync } from 'fs'; +import { join } from 'path'; +import { tmpdir } from 'os'; describe('use-tag command', () => { let testDir; - let tasksPath; + let helpers; beforeEach(async () => { - const setup = await setupTestEnvironment(); - testDir = setup.testDir; - tasksPath = setup.tasksPath; + // Create test directory + testDir = mkdtempSync(join(tmpdir(), 'task-master-use-tag-')); + + // Initialize test helpers + const context = global.createTestContext('use-tag'); + helpers = context.helpers; + + // Copy .env file if it exists + const mainEnvPath = join(process.cwd(), '.env'); + const testEnvPath = join(testDir, '.env'); + if (existsSync(mainEnvPath)) { + const envContent = readFileSync(mainEnvPath, 'utf8'); + writeFileSync(testEnvPath, envContent); + } + + // Initialize task-master project + const initResult = await helpers.taskMaster('init', ['-y'], { + cwd: testDir + }); + expect(initResult).toHaveExitCode(0); + + // Create tasks file path + const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json'); // Create a test project with multiple tags const tasksData = { - tasks: [ - { - id: 1, - description: 'Task in master', - status: 'pending', - tags: ['master'] - }, - { - id: 2, - description: 'Task in feature', - status: 'pending', - tags: ['feature'] - }, - { - id: 3, - description: 'Task in both', - status: 'pending', - tags: ['master', 'feature'] - } - ], - tags: { - master: { - name: 'master', - description: 'Main development branch' - }, - feature: { - name: 'feature', - description: 'Feature branch' - }, - release: { - name: 'release', - description: 'Release branch' - } + master: { + tasks: [ + { + id: 1, + description: 'Task in master', + status: 'pending', + tags: ['master'] + }, + { + id: 3, + description: 'Task in both', + status: 'pending', + tags: ['master', 'feature'] + } + ] + }, + feature: { + tasks: [ + { + id: 2, + description: 'Task in feature', + status: 'pending', + tags: ['feature'] + }, + { + id: 3, + description: 'Task in both', + status: 'pending', + tags: ['master', 'feature'] + } + ] + }, + release: { + tasks: [] }, - activeTag: 'master', metadata: { - nextId: 4 + nextId: 4, + activeTag: 'master' } }; - fs.writeFileSync(tasksPath, JSON.stringify(tasksData, null, 2)); + writeFileSync(tasksPath, JSON.stringify(tasksData, null, 2)); }); afterEach(async () => { - await cleanupTestEnvironment(testDir); + // Clean up test directory + if (testDir && existsSync(testDir)) { + rmSync(testDir, { recursive: true, force: true }); + } }); - test('should switch to an existing tag', async () => { - const result = await runCommand(['use-tag', 'feature'], testDir); + it('should switch to an existing tag', async () => { + const result = await helpers.taskMaster('use-tag', ['feature'], { + cwd: testDir + }); - expect(result.code).toBe(0); - expect(result.stdout).toContain('Successfully switched to tag: feature'); + expect(result).toHaveExitCode(0); + expect(result.stdout).toContain('Successfully switched to tag "feature"'); - // Verify the active tag was updated - const updatedData = JSON.parse(fs.readFileSync(tasksPath, 'utf8')); - expect(updatedData.activeTag).toBe('feature'); + // Verify the active tag was updated in state.json + const statePath = join(testDir, '.taskmaster/state.json'); + const stateData = JSON.parse(readFileSync(statePath, 'utf8')); + expect(stateData.currentTag).toBe('feature'); }); - test('should show error when switching to non-existent tag', async () => { - const result = await runCommand(['use-tag', 'nonexistent'], testDir); + it('should show error when switching to non-existent tag', async () => { + const result = await helpers.taskMaster('use-tag', ['nonexistent'], { + cwd: testDir + }); - expect(result.code).toBe(1); + expect(result).toHaveExitCode(1); expect(result.stderr).toContain('Tag "nonexistent" does not exist'); }); - test('should switch from feature tag back to master', async () => { + it('should switch from feature tag back to master', async () => { // First switch to feature - await runCommand(['use-tag', 'feature'], testDir); + await helpers.taskMaster('use-tag', ['feature'], { cwd: testDir }); // Then switch back to master - const result = await runCommand(['use-tag', 'master'], testDir); + const result = await helpers.taskMaster('use-tag', ['master'], { + cwd: testDir + }); - expect(result.code).toBe(0); - expect(result.stdout).toContain('Successfully switched to tag: master'); + expect(result).toHaveExitCode(0); + expect(result.stdout).toContain('Successfully switched to tag "master"'); - const updatedData = JSON.parse(fs.readFileSync(tasksPath, 'utf8')); - expect(updatedData.activeTag).toBe('master'); + // Verify the active tag was updated in state.json + const statePath = join(testDir, '.taskmaster/state.json'); + const stateData = JSON.parse(readFileSync(statePath, 'utf8')); + expect(stateData.currentTag).toBe('master'); }); - test('should handle switching to the same tag gracefully', async () => { - const result = await runCommand(['use-tag', 'master'], testDir); + it('should handle switching to the same tag gracefully', async () => { + const result = await helpers.taskMaster('use-tag', ['master'], { + cwd: testDir + }); - expect(result.code).toBe(0); - expect(result.stdout).toContain('Already on tag: master'); + expect(result).toHaveExitCode(0); + expect(result.stdout).toContain('Successfully switched to tag "master"'); }); - test('should work with custom tasks file path', async () => { - const customTasksPath = path.join(testDir, 'custom-tasks.json'); - fs.copyFileSync(tasksPath, customTasksPath); + it('should work with custom tasks file path', async () => { + const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json'); + const customTasksPath = join(testDir, 'custom-tasks.json'); + const content = readFileSync(tasksPath, 'utf8'); + writeFileSync(customTasksPath, content); - const result = await runCommand( - ['use-tag', 'feature', '-f', customTasksPath], - testDir + const result = await helpers.taskMaster( + 'use-tag', + ['feature', '-f', customTasksPath], + { cwd: testDir } ); - expect(result.code).toBe(0); - expect(result.stdout).toContain('Successfully switched to tag: feature'); + expect(result).toHaveExitCode(0); + expect(result.stdout).toContain('Successfully switched to tag "feature"'); - const updatedData = JSON.parse(fs.readFileSync(customTasksPath, 'utf8')); - expect(updatedData.activeTag).toBe('feature'); + // Verify the active tag was updated in state.json + const statePath = join(testDir, '.taskmaster/state.json'); + const stateData = JSON.parse(readFileSync(statePath, 'utf8')); + expect(stateData.currentTag).toBe('feature'); }); - test('should fail when tasks file does not exist', async () => { - const nonExistentPath = path.join(testDir, 'nonexistent.json'); - const result = await runCommand( - ['use-tag', 'feature', '-f', nonExistentPath], - testDir + it('should fail when tasks file does not exist', async () => { + const nonExistentPath = join(testDir, 'nonexistent.json'); + const result = await helpers.taskMaster( + 'use-tag', + ['feature', '-f', nonExistentPath], + { cwd: testDir } ); - expect(result.code).toBe(1); - expect(result.stderr).toContain('Tasks file not found'); + expect(result).toHaveExitCode(1); + expect(result.stderr).toContain('does not exist'); }); }); \ No newline at end of file diff --git a/tests/e2e/utils/logger.js b/tests/e2e/utils/logger.js index 9a920700..c503800c 100644 --- a/tests/e2e/utils/logger.js +++ b/tests/e2e/utils/logger.js @@ -58,7 +58,10 @@ export class TestLogger { break; } - console.log(coloredMessage); + // Only output to console if debugging or it's an error + if ((process.env.DEBUG_TESTS || level === 'ERROR') && !process.env.JEST_SILENT_MODE) { + console.log(coloredMessage); + } // Write to file if immediate flush requested if (options.flush) {