diff --git a/src/actions/StepPerformer.test.ts b/src/actions/StepPerformer.test.ts index 3901031..6ef5eaf 100644 --- a/src/actions/StepPerformer.test.ts +++ b/src/actions/StepPerformer.test.ts @@ -4,9 +4,10 @@ import { CodeEvaluator } from '@/utils/CodeEvaluator'; import { SnapshotManager } from '@/utils/SnapshotManager'; import { PromptHandler, TestingFrameworkAPICatalog } from '@/types'; import * as fs from 'fs'; +import * as crypto from 'crypto'; -// Mock the 'fs' module to prevent actual file system operations during tests jest.mock('fs'); +jest.mock('crypto'); describe('StepPerformer', () => { let stepPerformer: StepPerformer; @@ -91,11 +92,18 @@ describe('StepPerformer', () => { mockPromptHandler.runPrompt.mockResolvedValue(promptResult); mockCodeEvaluator.evaluate.mockResolvedValue(codeEvaluationResult); + const viewHierarchyHash = 'hash'; + (crypto.createHash as jest.Mock).mockReturnValue({ + update: jest.fn().mockReturnValue({ + digest: jest.fn().mockReturnValue(viewHierarchyHash), + }), + }); + // Adjust fs mocks based on cacheExists if (cacheExists) { (fs.existsSync as jest.Mock).mockReturnValue(true); const cacheData = {}; - const cacheKey = JSON.stringify({ step: 'tap button', previous: [] }); + const cacheKey = JSON.stringify({ step: 'tap button', previous: [], viewHierarchyHash }); // @ts-ignore cacheData[cacheKey] = promptResult; (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(cacheData)); diff --git a/src/actions/StepPerformer.ts b/src/actions/StepPerformer.ts index 964d38f..f31f871 100644 --- a/src/actions/StepPerformer.ts +++ b/src/actions/StepPerformer.ts @@ -1,9 +1,10 @@ import { PromptCreator } from '@/utils/PromptCreator'; import { CodeEvaluator } from '@/utils/CodeEvaluator'; import { SnapshotManager } from '@/utils/SnapshotManager'; -import {CodeEvaluationResult, PreviousStep, PromptHandler} from '@/types'; +import {CodeEvaluationResult, PreviousStep, PromptHandler, TestingFrameworkDriver} from '@/types'; import * as fs from 'fs'; import * as path from 'path'; +import * as crypto from 'crypto'; export class StepPerformer { private cache: Map = new Map(); @@ -20,8 +21,9 @@ export class StepPerformer { this.cacheFilePath = path.resolve(process.cwd(), cacheFileName); } - private getCacheKey(step: string, previous: PreviousStep[]): string { - return JSON.stringify({ step, previous }); + private generateCacheKey(step: string, previous: PreviousStep[], viewHierarchy: string): string { + const viewHierarchyHash = crypto.createHash('md5').update(viewHierarchy).digest('hex'); + return JSON.stringify({ step, previous, viewHierarchyHash }); } private loadCacheFromFile(): void { @@ -61,7 +63,7 @@ export class StepPerformer { const isSnapshotImageAttached = snapshot != null && this.promptHandler.isSnapshotImageSupported(); - const cacheKey = this.getCacheKey(step, previous); + const cacheKey = this.generateCacheKey(step, previous, viewHierarchy); if (this.cache.has(cacheKey)) { const cachedPromptResult = this.cache.get(cacheKey); @@ -96,7 +98,7 @@ export class StepPerformer { result: undefined, }]; - const retryCacheKey = this.getCacheKey(step, newPrevious); + const retryCacheKey = this.generateCacheKey(step, newPrevious, viewHierarchy); if (this.cache.has(retryCacheKey)) { const cachedRetryPromptResult = this.cache.get(retryCacheKey); diff --git a/src/integration tests/index.test.ts b/src/integration tests/index.test.ts index 876eeb5..37a78ac 100644 --- a/src/integration tests/index.test.ts +++ b/src/integration tests/index.test.ts @@ -3,9 +3,11 @@ import { Copilot } from "@/Copilot"; import { PromptHandler, TestingFrameworkDriver } from "@/types"; import fs from 'fs'; import path from 'path'; +import * as crypto from 'crypto'; jest.mock('fs'); jest.mock('path'); +jest.mock('crypto'); describe('Copilot Integration Tests', () => { let mockFrameworkDriver: jest.Mocked; @@ -37,6 +39,12 @@ describe('Copilot Integration Tests', () => { mockFs.readFileSync.mockReturnValue('{}'); mockFs.writeFileSync.mockImplementation(() => {}); mockPath.resolve.mockImplementation((...paths) => paths.join('/')); + + (crypto.createHash as jest.Mock).mockReturnValue({ + update: jest.fn().mockReturnValue({ + digest: jest.fn().mockReturnValue('hash'), + }), + }); }); afterEach(() => { @@ -245,7 +253,7 @@ describe('Copilot Integration Tests', () => { it('should read from existing cache file', async () => { mockFs.existsSync.mockReturnValue(true); mockFs.readFileSync.mockReturnValue(JSON.stringify({ - '{"step":"Cached action","previous":[]}': '// Cached action code' + '{"step":"Cached action","previous":[],"viewHierarchyHash":"hash"}': '// Cached action code' })); await copilot.perform('Cached action');