refactor
This commit is contained in:
@@ -9,6 +9,9 @@
|
|||||||
"ecmaVersion": 2022
|
"ecmaVersion": 2022
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"typescript": {}
|
||||||
|
},
|
||||||
"boundaries/elements": [
|
"boundaries/elements": [
|
||||||
{
|
{
|
||||||
"type": "website",
|
"type": "website",
|
||||||
@@ -20,11 +23,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "adapters",
|
"type": "adapters",
|
||||||
"pattern": "adapters/**/*"
|
"pattern": ["adapters/**/*", "@adapters/**/*"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "core",
|
"type": "core",
|
||||||
"pattern": "core/**/*"
|
"pattern": ["core/**/*", "@core/**/*"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,136 +1,97 @@
|
|||||||
# 🧭 Orchestrator
|
# 🧭 Orchestrator
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
Interpret the user’s intent, collect all required context,
|
Interpret the user’s intent, gather context, maintain the TODO list,
|
||||||
decide **what must be done**, and delegate **clearly scoped tasks** to the correct expert.
|
and delegate work in the correct order.
|
||||||
|
|
||||||
The Orchestrator does NOT:
|
The Orchestrator coordinates.
|
||||||
- perform expert work
|
It does not execute.
|
||||||
- analyze architecture
|
|
||||||
- design solutions
|
|
||||||
- implement logic
|
|
||||||
- define expert output formats
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## User Supremacy
|
## User Supremacy
|
||||||
- The user is the absolute highest authority.
|
- The user is the highest authority.
|
||||||
- Any user instruction overrides all rules and processes.
|
- Any new user instruction immediately interrupts all ongoing work.
|
||||||
- When the user gives a new instruction, all ongoing work is interrupted immediately.
|
- No reinterpretation, no negotiation.
|
||||||
- No reinterpretation, no negotiation, no resistance.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Context Responsibility (Critical)
|
## TODO List Is the Source of Truth (Critical)
|
||||||
The Orchestrator is the **only role allowed to gather or interpret context**.
|
The TODO list represents **all remaining work**.
|
||||||
|
|
||||||
The Orchestrator MUST:
|
Rules:
|
||||||
- identify all relevant files
|
- Only the Orchestrator owns the TODO list.
|
||||||
- identify which parts are already done
|
- Experts never modify TODOs directly.
|
||||||
- identify what is still missing
|
- The Orchestrator must keep the TODO list accurate at all times.
|
||||||
- extract only relevant information
|
|
||||||
- pass **complete, explicit context** to experts
|
|
||||||
|
|
||||||
Experts MUST NOT:
|
|
||||||
- scan the repository
|
|
||||||
- infer missing information
|
|
||||||
- rediscover context
|
|
||||||
|
|
||||||
If an expert requests missing context, the Orchestrator must provide it immediately.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Domain Routing (Mandatory)
|
## Handling Expert Results (Mandatory)
|
||||||
Before delegating any task, the Orchestrator MUST decide **exactly one domain**:
|
When an expert returns a result, the Orchestrator MUST do the following **in order**:
|
||||||
|
|
||||||
- Frontend
|
1. **Read “What is still open”**
|
||||||
OR
|
2. If it is:
|
||||||
- Backend (Coder)
|
- “Nothing” → proceed
|
||||||
|
- a list of items → STOP and update the TODO list
|
||||||
|
3. Convert each open item into a TODO entry
|
||||||
|
4. Remove completed TODOs
|
||||||
|
5. Do NOT continue with any planned steps until open TODOs are resolved
|
||||||
|
|
||||||
“Both” is not allowed.
|
The Orchestrator MUST NOT:
|
||||||
|
- ignore open items
|
||||||
If a user instruction touches frontend and backend:
|
- assume they are handled later
|
||||||
- split into separate tasks
|
- continue a multi-step plan automatically
|
||||||
- delegate each task independently
|
- delegate the next planned step while TODOs exist
|
||||||
- never mix domains in a single task
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Information Gathering Order
|
## Progress Rule (Hard Gate)
|
||||||
The Orchestrator MUST NOT rush to Architect or Coder.
|
The Orchestrator may proceed to the next step ONLY when:
|
||||||
|
|
||||||
Before delegating, the Orchestrator MUST check:
|
- the current TODO list is empty
|
||||||
|
OR
|
||||||
|
- the user explicitly instructs to ignore open items
|
||||||
|
|
||||||
1. Is the user intent clear?
|
No other condition allows forward progress.
|
||||||
2. Is all required information available?
|
|
||||||
3. Are assumptions being made?
|
|
||||||
4. Is this the correct next step?
|
|
||||||
|
|
||||||
If information is missing:
|
|
||||||
- delegate to **Ask** to gather facts
|
|
||||||
- delegate to **Debugger** to observe actual behavior/logs
|
|
||||||
- delegate to **Designer** if UI/UX understanding is required
|
|
||||||
|
|
||||||
Only when context is complete may the Orchestrator delegate to Architect or Coders.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Task Grouping
|
## Delegation Rule
|
||||||
The Orchestrator MUST:
|
When delegating:
|
||||||
- bundle work that belongs to the same goal and same domain
|
- delegate ONLY the next TODO
|
||||||
- split work that does not belong together
|
- include full context
|
||||||
- avoid micro-tasks
|
- scope to a single purpose
|
||||||
- avoid over-scoped tasks
|
- assign to exactly one expert
|
||||||
|
|
||||||
Each delegated task must be:
|
Never delegate multiple TODOs at once unless they are explicitly bundled.
|
||||||
- single-purpose
|
|
||||||
- fully scoped
|
|
||||||
- executable without guessing
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## TODO List (Required)
|
## Planning Discipline
|
||||||
When the user gives a task, the Orchestrator MUST:
|
The Orchestrator must NOT treat a plan as a script.
|
||||||
|
|
||||||
1. Create a TODO list containing **only outstanding work**.
|
Plans are:
|
||||||
2. Exclude anything already completed.
|
- intentions
|
||||||
3. Update the TODO list after every expert result.
|
- not guarantees
|
||||||
4. Remove completed items immediately.
|
- always subordinate to actual execution results
|
||||||
5. Never invent TODOs.
|
|
||||||
|
|
||||||
The TODO list is the **single source of truth** for remaining work.
|
Reality (expert output) always overrides plan order.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Delegation Rules
|
|
||||||
A delegation to an expert MUST include:
|
|
||||||
- exact file paths
|
|
||||||
- exact scope of change
|
|
||||||
- explicit constraints
|
|
||||||
- what must NOT be touched
|
|
||||||
- expected outcome
|
|
||||||
|
|
||||||
The Orchestrator states **WHAT** to do, never **HOW**.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Forbidden
|
## Forbidden
|
||||||
The Orchestrator MUST NOT:
|
The Orchestrator MUST NOT:
|
||||||
- perform expert analysis
|
- continue a plan when TODOs remain
|
||||||
- solve problems itself
|
- treat “still open” as informational only
|
||||||
- explain architecture or design
|
- assume the expert will handle remaining work implicitly
|
||||||
- define deliverables for experts
|
- batch unresolved items
|
||||||
- define expert completion formats
|
- skip TODO reconciliation
|
||||||
- rush to execution
|
- push work forward “for momentum”
|
||||||
- delegate with incomplete context
|
|
||||||
- merge frontend and backend work
|
|
||||||
- generate long explanations
|
|
||||||
- override user intent
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Completion
|
## Completion
|
||||||
A step is complete when:
|
A workflow step is complete ONLY when:
|
||||||
- the expert finishes the delegated task
|
- the expert reports “What is still open: Nothing”
|
||||||
- the TODO list reflects only remaining work
|
- the TODO list is empty
|
||||||
- the Orchestrator either delegates the next task or waits for user input
|
- or the user explicitly overrides and says to proceed anyway
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { AnalyticsSnapshotOrmEntity } from '../../../../../adapters/persistence/typeorm/analytics/AnalyticsSnapshotOrmEntity';
|
import { AnalyticsSnapshotOrmEntity } from '../../../../..//persistence/typeorm/analytics/AnalyticsSnapshotOrmEntity';
|
||||||
import { EngagementOrmEntity } from '../../../../../adapters/persistence/typeorm/analytics/EngagementOrmEntity';
|
import { EngagementOrmEntity } from '../../../../..//persistence/typeorm/analytics/EngagementOrmEntity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Global, Module } from '@nestjs/common';
|
import { Global, Module } from '@nestjs/common';
|
||||||
import { Logger } from '@gridpilot/shared/application/Logger';
|
import { Logger } from '@gridpilot/shared/application/Logger';
|
||||||
import { ConsoleLogger } from '@gridpilot/adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '@gridpilot//logging/ConsoleLogger';
|
||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
|
|||||||
@@ -1,20 +1,26 @@
|
|||||||
import { Provider } from '@nestjs/common';
|
import { Provider } from '@nestjs/common';
|
||||||
import { AnalyticsService } from './AnalyticsService';
|
import { AnalyticsService } from './AnalyticsService';
|
||||||
|
import { RecordPageViewUseCase } from './use-cases/RecordPageViewUseCase';
|
||||||
|
import { RecordEngagementUseCase } from './use-cases/RecordEngagementUseCase';
|
||||||
|
|
||||||
const Logger_TOKEN = 'Logger_TOKEN';
|
const Logger_TOKEN = 'Logger_TOKEN';
|
||||||
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
|
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
|
||||||
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
|
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
|
||||||
|
const RECORD_PAGE_VIEW_USE_CASE_TOKEN = 'RecordPageViewUseCase_TOKEN';
|
||||||
|
const RECORD_ENGAGEMENT_USE_CASE_TOKEN = 'RecordEngagementUseCase_TOKEN';
|
||||||
|
|
||||||
import { Logger } from '@gridpilot/shared/logging/Logger';
|
import { Logger } from '@gridpilot/shared/logging/Logger';
|
||||||
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
|
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
|
||||||
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
|
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
|
||||||
|
|
||||||
import { ConsoleLogger } from '../../../../adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '../../../..//logging/ConsoleLogger';
|
||||||
import { InMemoryPageViewRepository } from '../../../../adapters/analytics/persistence/inmemory/InMemoryPageViewRepository';
|
import { InMemoryPageViewRepository } from '../../../..//analytics/persistence/inmemory/InMemoryPageViewRepository';
|
||||||
import { InMemoryEngagementRepository } from '../../../../adapters/analytics/persistence/inmemory/InMemoryEngagementRepository';
|
import { InMemoryEngagementRepository } from '../../../..//analytics/persistence/inmemory/InMemoryEngagementRepository';
|
||||||
|
|
||||||
export const AnalyticsProviders: Provider[] = [
|
export const AnalyticsProviders: Provider[] = [
|
||||||
AnalyticsService,
|
AnalyticsService,
|
||||||
|
RecordPageViewUseCase,
|
||||||
|
RecordEngagementUseCase,
|
||||||
{
|
{
|
||||||
provide: Logger_TOKEN,
|
provide: Logger_TOKEN,
|
||||||
useClass: ConsoleLogger,
|
useClass: ConsoleLogger,
|
||||||
@@ -27,4 +33,12 @@ export const AnalyticsProviders: Provider[] = [
|
|||||||
provide: IENGAGEMENT_REPO_TOKEN,
|
provide: IENGAGEMENT_REPO_TOKEN,
|
||||||
useClass: InMemoryEngagementRepository,
|
useClass: InMemoryEngagementRepository,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: RECORD_PAGE_VIEW_USE_CASE_TOKEN,
|
||||||
|
useClass: RecordPageViewUseCase,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RECORD_ENGAGEMENT_USE_CASE_TOKEN,
|
||||||
|
useClass: RecordEngagementUseCase,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
@@ -1,83 +1,26 @@
|
|||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
import { RecordEngagementInput, RecordEngagementOutput, RecordPageViewInput, RecordPageViewOutput } from './dto/AnalyticsDto';
|
import { RecordEngagementInput, RecordEngagementOutput, RecordPageViewInput, RecordPageViewOutput } from './dto/AnalyticsDto';
|
||||||
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
|
|
||||||
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
|
|
||||||
import { Logger } from '@gridpilot/shared/logging/Logger';
|
import { Logger } from '@gridpilot/shared/logging/Logger';
|
||||||
import { PageView } from '@gridpilot/analytics/domain/entities/PageView';
|
import { RecordPageViewUseCase } from './use-cases/RecordPageViewUseCase';
|
||||||
import { EngagementEvent } from '@gridpilot/analytics/domain/entities/EngagementEvent';
|
import { RecordEngagementUseCase } from './use-cases/RecordEngagementUseCase';
|
||||||
|
|
||||||
const Logger_TOKEN = 'Logger_TOKEN';
|
const Logger_TOKEN = 'Logger_TOKEN';
|
||||||
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
|
const RECORD_PAGE_VIEW_USE_CASE_TOKEN = 'RecordPageViewUseCase_TOKEN';
|
||||||
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
|
const RECORD_ENGAGEMENT_USE_CASE_TOKEN = 'RecordEngagementUseCase_TOKEN';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AnalyticsService {
|
export class AnalyticsService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IPAGE_VIEW_REPO_TOKEN) private readonly pageViewRepository: IPageViewRepository,
|
@Inject(RECORD_PAGE_VIEW_USE_CASE_TOKEN) private readonly recordPageViewUseCase: RecordPageViewUseCase,
|
||||||
@Inject(IENGAGEMENT_REPO_TOKEN) private readonly engagementRepository: IEngagementRepository,
|
@Inject(RECORD_ENGAGEMENT_USE_CASE_TOKEN) private readonly recordEngagementUseCase: RecordEngagementUseCase,
|
||||||
@Inject(Logger_TOKEN) private readonly logger: Logger,
|
@Inject(Logger_TOKEN) private readonly logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async recordPageView(input: RecordPageViewInput): Promise<RecordPageViewOutput> {
|
async recordPageView(input: RecordPageViewInput): Promise<RecordPageViewOutput> {
|
||||||
this.logger.debug('Executing RecordPageViewUseCase', { input });
|
return await this.recordPageViewUseCase.execute(input);
|
||||||
try {
|
|
||||||
const pageViewId = `pv-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
||||||
|
|
||||||
const baseProps: Omit<Parameters<typeof PageView.create>[0], 'timestamp'> = {
|
|
||||||
id: pageViewId,
|
|
||||||
entityType: input.entityType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
|
|
||||||
entityId: input.entityId,
|
|
||||||
visitorType: input.visitorType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
|
|
||||||
sessionId: input.sessionId,
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageView = PageView.create({
|
|
||||||
...baseProps,
|
|
||||||
...(input.visitorId !== undefined ? { visitorId: input.visitorId } : {}),
|
|
||||||
...(input.referrer !== undefined ? { referrer: input.referrer } : {}),
|
|
||||||
...(input.userAgent !== undefined ? { userAgent: input.userAgent } : {}),
|
|
||||||
...(input.country !== undefined ? { country: input.country } : {}),
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.pageViewRepository.save(pageView);
|
|
||||||
this.logger.info('Page view recorded successfully', { pageViewId, input });
|
|
||||||
return { pageViewId };
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Error recording page view', error, { input });
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async recordEngagement(input: RecordEngagementInput): Promise<RecordEngagementOutput> {
|
async recordEngagement(input: RecordEngagementInput): Promise<RecordEngagementOutput> {
|
||||||
this.logger.debug('Executing RecordEngagementUseCase', { input });
|
return await this.recordEngagementUseCase.execute(input);
|
||||||
try {
|
|
||||||
const eventId = `eng-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
||||||
|
|
||||||
const baseProps: Omit<Parameters<typeof EngagementEvent.create>[0], 'timestamp'> = {
|
|
||||||
id: eventId,
|
|
||||||
action: input.action as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
|
|
||||||
entityType: input.entityType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
|
|
||||||
entityId: input.entityId,
|
|
||||||
actorType: input.actorType,
|
|
||||||
sessionId: input.sessionId,
|
|
||||||
};
|
|
||||||
|
|
||||||
const event = EngagementEvent.create({
|
|
||||||
...baseProps,
|
|
||||||
...(input.actorId !== undefined ? { actorId: input.actorId } : {}),
|
|
||||||
...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.engagementRepository.save(event);
|
|
||||||
this.logger.info('Engagement recorded successfully', { eventId, input });
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventId,
|
|
||||||
engagementWeight: event.getEngagementWeight(),
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Error recording engagement', error, { input });
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { RecordEngagementUseCase } from './RecordEngagementUseCase';
|
||||||
|
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
|
||||||
|
import { Logger } from '@gridpilot/shared/logging/Logger';
|
||||||
|
|
||||||
|
describe('RecordEngagementUseCase', () => {
|
||||||
|
let useCase: RecordEngagementUseCase;
|
||||||
|
let engagementRepository: jest.Mocked<IEngagementRepository>;
|
||||||
|
let logger: jest.Mocked<Logger>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
RecordEngagementUseCase,
|
||||||
|
{
|
||||||
|
provide: 'IEngagementRepository_TOKEN',
|
||||||
|
useValue: {
|
||||||
|
save: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: 'Logger_TOKEN',
|
||||||
|
useValue: {
|
||||||
|
debug: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
useCase = module.get<RecordEngagementUseCase>(RecordEngagementUseCase);
|
||||||
|
engagementRepository = module.get('IEngagementRepository_TOKEN');
|
||||||
|
logger = module.get('Logger_TOKEN');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('execute', () => {
|
||||||
|
it('should save the engagement event and return the eventId and engagementWeight', async () => {
|
||||||
|
const input = {
|
||||||
|
action: 'like' as any,
|
||||||
|
entityType: 'race' as any,
|
||||||
|
entityId: 'race-123',
|
||||||
|
actorType: 'driver',
|
||||||
|
sessionId: 'session-456',
|
||||||
|
actorId: 'actor-789',
|
||||||
|
metadata: { some: 'data' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockEvent = {
|
||||||
|
getEngagementWeight: jest.fn().mockReturnValue(10),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock the create function to return the mock event
|
||||||
|
const originalCreate = require('@gridpilot/analytics/domain/entities/EngagementEvent').EngagementEvent.create;
|
||||||
|
require('@gridpilot/analytics/domain/entities/EngagementEvent').EngagementEvent.create = jest.fn().mockReturnValue(mockEvent);
|
||||||
|
|
||||||
|
engagementRepository.save.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
const result = await useCase.execute(input);
|
||||||
|
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith('Executing RecordEngagementUseCase', { input });
|
||||||
|
expect(engagementRepository.save).toHaveBeenCalledWith(mockEvent);
|
||||||
|
expect(logger.info).toHaveBeenCalledWith('Engagement recorded successfully', expect.objectContaining({ eventId: expect.any(String), input }));
|
||||||
|
expect(result).toHaveProperty('eventId');
|
||||||
|
expect(result).toHaveProperty('engagementWeight', 10);
|
||||||
|
expect(typeof result.eventId).toBe('string');
|
||||||
|
|
||||||
|
// Restore original
|
||||||
|
require('@gridpilot/analytics/domain/entities/EngagementEvent').EngagementEvent.create = originalCreate;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle errors and throw them', async () => {
|
||||||
|
const input = {
|
||||||
|
action: 'like' as any,
|
||||||
|
entityType: 'race' as any,
|
||||||
|
entityId: 'race-123',
|
||||||
|
actorType: 'driver',
|
||||||
|
sessionId: 'session-456',
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = new Error('Save failed');
|
||||||
|
engagementRepository.save.mockRejectedValue(error);
|
||||||
|
|
||||||
|
await expect(useCase.execute(input)).rejects.toThrow('Save failed');
|
||||||
|
expect(logger.error).toHaveBeenCalledWith('Error recording engagement', error, { input });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
|
import { RecordEngagementInput, RecordEngagementOutput } from '../dto/AnalyticsDto';
|
||||||
|
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
|
||||||
|
import { Logger } from '@gridpilot/shared/logging/Logger';
|
||||||
|
import { EngagementEvent } from '@gridpilot/analytics/domain/entities/EngagementEvent';
|
||||||
|
|
||||||
|
const Logger_TOKEN = 'Logger_TOKEN';
|
||||||
|
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RecordEngagementUseCase {
|
||||||
|
constructor(
|
||||||
|
@Inject(IENGAGEMENT_REPO_TOKEN) private readonly engagementRepository: IEngagementRepository,
|
||||||
|
@Inject(Logger_TOKEN) private readonly logger: Logger,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(input: RecordEngagementInput): Promise<RecordEngagementOutput> {
|
||||||
|
this.logger.debug('Executing RecordEngagementUseCase', { input });
|
||||||
|
try {
|
||||||
|
const eventId = `eng-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
|
||||||
|
const baseProps: Omit<Parameters<typeof EngagementEvent.create>[0], 'timestamp'> = {
|
||||||
|
id: eventId,
|
||||||
|
action: input.action as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
|
||||||
|
entityType: input.entityType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
|
||||||
|
entityId: input.entityId,
|
||||||
|
actorType: input.actorType,
|
||||||
|
sessionId: input.sessionId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = EngagementEvent.create({
|
||||||
|
...baseProps,
|
||||||
|
...(input.actorId !== undefined ? { actorId: input.actorId } : {}),
|
||||||
|
...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.engagementRepository.save(event);
|
||||||
|
this.logger.info('Engagement recorded successfully', { eventId, input });
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventId,
|
||||||
|
engagementWeight: event.getEngagementWeight(),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Error recording engagement', error, { input });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { RecordPageViewUseCase } from './RecordPageViewUseCase';
|
||||||
|
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
|
||||||
|
import { Logger } from '@gridpilot/shared/logging/Logger';
|
||||||
|
|
||||||
|
describe('RecordPageViewUseCase', () => {
|
||||||
|
let useCase: RecordPageViewUseCase;
|
||||||
|
let pageViewRepository: jest.Mocked<IPageViewRepository>;
|
||||||
|
let logger: jest.Mocked<Logger>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
RecordPageViewUseCase,
|
||||||
|
{
|
||||||
|
provide: 'IPageViewRepository_TOKEN',
|
||||||
|
useValue: {
|
||||||
|
save: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: 'Logger_TOKEN',
|
||||||
|
useValue: {
|
||||||
|
debug: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
useCase = module.get<RecordPageViewUseCase>(RecordPageViewUseCase);
|
||||||
|
pageViewRepository = module.get('IPageViewRepository_TOKEN');
|
||||||
|
logger = module.get('Logger_TOKEN');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('execute', () => {
|
||||||
|
it('should save the page view and return the pageViewId', async () => {
|
||||||
|
const input = {
|
||||||
|
entityType: 'race' as any,
|
||||||
|
entityId: 'race-123',
|
||||||
|
visitorType: 'anonymous' as any,
|
||||||
|
sessionId: 'session-456',
|
||||||
|
visitorId: 'visitor-789',
|
||||||
|
referrer: 'https://example.com',
|
||||||
|
userAgent: 'Mozilla/5.0',
|
||||||
|
country: 'US',
|
||||||
|
};
|
||||||
|
|
||||||
|
pageViewRepository.save.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
const result = await useCase.execute(input);
|
||||||
|
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith('Executing RecordPageViewUseCase', { input });
|
||||||
|
expect(pageViewRepository.save).toHaveBeenCalledTimes(1);
|
||||||
|
expect(logger.info).toHaveBeenCalledWith('Page view recorded successfully', expect.objectContaining({ pageViewId: expect.any(String), input }));
|
||||||
|
expect(result).toHaveProperty('pageViewId');
|
||||||
|
expect(typeof result.pageViewId).toBe('string');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle errors and throw them', async () => {
|
||||||
|
const input = {
|
||||||
|
entityType: 'race' as any,
|
||||||
|
entityId: 'race-123',
|
||||||
|
visitorType: 'anonymous' as any,
|
||||||
|
sessionId: 'session-456',
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = new Error('Save failed');
|
||||||
|
pageViewRepository.save.mockRejectedValue(error);
|
||||||
|
|
||||||
|
await expect(useCase.execute(input)).rejects.toThrow('Save failed');
|
||||||
|
expect(logger.error).toHaveBeenCalledWith('Error recording page view', error, { input });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
|
import { RecordPageViewInput, RecordPageViewOutput } from '../dto/AnalyticsDto';
|
||||||
|
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
|
||||||
|
import { Logger } from '@gridpilot/shared/logging/Logger';
|
||||||
|
import { PageView } from '@gridpilot/analytics/domain/entities/PageView';
|
||||||
|
|
||||||
|
const Logger_TOKEN = 'Logger_TOKEN';
|
||||||
|
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RecordPageViewUseCase {
|
||||||
|
constructor(
|
||||||
|
@Inject(IPAGE_VIEW_REPO_TOKEN) private readonly pageViewRepository: IPageViewRepository,
|
||||||
|
@Inject(Logger_TOKEN) private readonly logger: Logger,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(input: RecordPageViewInput): Promise<RecordPageViewOutput> {
|
||||||
|
this.logger.debug('Executing RecordPageViewUseCase', { input });
|
||||||
|
try {
|
||||||
|
const pageViewId = `pv-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
|
||||||
|
const baseProps: Omit<Parameters<typeof PageView.create>[0], 'timestamp'> = {
|
||||||
|
id: pageViewId,
|
||||||
|
entityType: input.entityType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
|
||||||
|
entityId: input.entityId,
|
||||||
|
visitorType: input.visitorType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
|
||||||
|
sessionId: input.sessionId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageView = PageView.create({
|
||||||
|
...baseProps,
|
||||||
|
...(input.visitorId !== undefined ? { visitorId: input.visitorId } : {}),
|
||||||
|
...(input.referrer !== undefined ? { referrer: input.referrer } : {}),
|
||||||
|
...(input.userAgent !== undefined ? { userAgent: input.userAgent } : {}),
|
||||||
|
...(input.country !== undefined ? { country: input.country } : {}),
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.pageViewRepository.save(pageView);
|
||||||
|
this.logger.info('Page view recorded successfully', { pageViewId, input });
|
||||||
|
return { pageViewId };
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Error recording page view', error, { input });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,12 +7,12 @@ import { IUserRepository, StoredUser } from '@gridpilot/core/identity/domain/rep
|
|||||||
import { IPasswordHashingService } from '@gridpilot/core/identity/domain/services/PasswordHashingService';
|
import { IPasswordHashingService } from '@gridpilot/core/identity/domain/services/PasswordHashingService';
|
||||||
import { Logger } from '@gridpilot/core/shared/logging/Logger';
|
import { Logger } from '@gridpilot/core/shared/logging/Logger';
|
||||||
|
|
||||||
import { InMemoryAuthRepository } from '../../../adapters/identity/persistence/inmemory/InMemoryAuthRepository';
|
import { InMemoryAuthRepository } from '../../..//identity/persistence/inmemory/InMemoryAuthRepository';
|
||||||
import { InMemoryUserRepository } from '../../../adapters/identity/persistence/inmemory/InMemoryUserRepository';
|
import { InMemoryUserRepository } from '../../..//identity/persistence/inmemory/InMemoryUserRepository';
|
||||||
import { InMemoryPasswordHashingService } from '../../../adapters/identity/services/InMemoryPasswordHashingService';
|
import { InMemoryPasswordHashingService } from '../../..//identity/services/InMemoryPasswordHashingService';
|
||||||
import { ConsoleLogger } from '../../../adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '../../..//logging/ConsoleLogger';
|
||||||
import { IdentitySessionPort } from '../../../../core/identity/application/ports/IdentitySessionPort'; // Path from apps/api/src/modules/auth
|
import { IdentitySessionPort } from '../../../../core/identity/application/ports/IdentitySessionPort'; // Path from apps/api/src/modules/auth
|
||||||
import { CookieIdentitySessionAdapter } from '../../../adapters/identity/session/CookieIdentitySessionAdapter';
|
import { CookieIdentitySessionAdapter } from '../../..//identity/session/CookieIdentitySessionAdapter';
|
||||||
|
|
||||||
// Define the tokens for dependency injection
|
// Define the tokens for dependency injection
|
||||||
export const AUTH_REPOSITORY_TOKEN = 'IAuthRepository';
|
export const AUTH_REPOSITORY_TOKEN = 'IAuthRepository';
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ import { GetTotalDriversUseCase } from '../../../../core/racing/application/use-
|
|||||||
import { CompleteDriverOnboardingUseCase } from '../../../../core/racing/application/use-cases/CompleteDriverOnboardingUseCase';
|
import { CompleteDriverOnboardingUseCase } from '../../../../core/racing/application/use-cases/CompleteDriverOnboardingUseCase';
|
||||||
|
|
||||||
// Import concrete in-memory implementations
|
// Import concrete in-memory implementations
|
||||||
import { InMemoryDriverRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
import { InMemoryDriverRepository } from '../../..//racing/persistence/inmemory/InMemoryDriverRepository';
|
||||||
import { InMemoryRankingService } from '../../../adapters/racing/services/InMemoryRankingService';
|
import { InMemoryRankingService } from '../../..//racing/services/InMemoryRankingService';
|
||||||
import { InMemoryDriverStatsService } from '../../../adapters/racing/services/InMemoryDriverStatsService';
|
import { InMemoryDriverStatsService } from '../../..//racing/services/InMemoryDriverStatsService';
|
||||||
import { InMemoryDriverRatingProvider } from '../../../adapters/racing/ports/InMemoryDriverRatingProvider';
|
import { InMemoryDriverRatingProvider } from '../../..//racing/ports/InMemoryDriverRatingProvider';
|
||||||
import { InMemoryImageServiceAdapter } from '../../../adapters/media/ports/InMemoryImageServiceAdapter';
|
import { InMemoryImageServiceAdapter } from '../../..//media/ports/InMemoryImageServiceAdapter';
|
||||||
import { InMemoryRaceRegistrationRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
import { InMemoryRaceRegistrationRepository } from '../../..//racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
||||||
import { InMemoryNotificationPreferenceRepository } from '../../../adapters/notifications/persistence/inmemory/InMemoryNotificationPreferenceRepository';
|
import { InMemoryNotificationPreferenceRepository } from '../../..//notifications/persistence/inmemory/InMemoryNotificationPreferenceRepository';
|
||||||
import { ConsoleLogger } from '../../../adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '../../..//logging/ConsoleLogger';
|
||||||
|
|
||||||
// Define injection tokens
|
// Define injection tokens
|
||||||
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ import { LeagueService } from './LeagueService';
|
|||||||
import { Logger } from '@gridpilot/shared/application/Logger';
|
import { Logger } from '@gridpilot/shared/application/Logger';
|
||||||
|
|
||||||
// Import concrete in-memory implementations
|
// Import concrete in-memory implementations
|
||||||
import { InMemoryLeagueRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
import { InMemoryLeagueRepository } from '/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||||
import { InMemoryLeagueMembershipRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
|
import { InMemoryLeagueMembershipRepository } from '/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
|
||||||
import { InMemoryLeagueStandingsRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueStandingsRepository';
|
import { InMemoryLeagueStandingsRepository } from '/racing/persistence/inmemory/InMemoryLeagueStandingsRepository';
|
||||||
import { InMemorySeasonRepository } from 'adapters/racing/persistence/inmemory/InMemorySeasonRepository';
|
import { InMemorySeasonRepository } from '/racing/persistence/inmemory/InMemorySeasonRepository';
|
||||||
import { InMemoryLeagueScoringConfigRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueScoringConfigRepository';
|
import { InMemoryLeagueScoringConfigRepository } from '/racing/persistence/inmemory/InMemoryLeagueScoringConfigRepository';
|
||||||
import { InMemoryGameRepository } from 'adapters/racing/persistence/inmemory/InMemoryGameRepository';
|
import { InMemoryGameRepository } from '/racing/persistence/inmemory/InMemoryGameRepository';
|
||||||
import { InMemoryProtestRepository } from 'adapters/racing/persistence/inmemory/InMemoryProtestRepository';
|
import { InMemoryProtestRepository } from '/racing/persistence/inmemory/InMemoryProtestRepository';
|
||||||
import { InMemoryRaceRepository } from 'adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
import { InMemoryRaceRepository } from '/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||||
import { InMemoryDriverRepository } from 'adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
import { InMemoryDriverRepository } from '/racing/persistence/inmemory/InMemoryDriverRepository';
|
||||||
import { InMemoryStandingRepository } from 'adapters/racing/persistence/inmemory/InMemoryStandingRepository';
|
import { InMemoryStandingRepository } from '/racing/persistence/inmemory/InMemoryStandingRepository';
|
||||||
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '/logging/ConsoleLogger';
|
||||||
|
|
||||||
// Import use cases
|
// Import use cases
|
||||||
import { GetAllLeaguesWithCapacityUseCase } from '@gridpilot/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
|
import { GetAllLeaguesWithCapacityUseCase } from '@gridpilot/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ import { GetWalletUseCase } from '@gridpilot/payments/application/use-cases/GetW
|
|||||||
import { ProcessWalletTransactionUseCase } from '@gridpilot/payments/application/use-cases/ProcessWalletTransactionUseCase';
|
import { ProcessWalletTransactionUseCase } from '@gridpilot/payments/application/use-cases/ProcessWalletTransactionUseCase';
|
||||||
|
|
||||||
// Import concrete in-memory implementations
|
// Import concrete in-memory implementations
|
||||||
import { InMemoryPaymentRepository } from 'adapters/payments/persistence/inmemory/InMemoryPaymentRepository';
|
import { InMemoryPaymentRepository } from '/payments/persistence/inmemory/InMemoryPaymentRepository';
|
||||||
import { InMemoryMembershipFeeRepository, InMemoryMemberPaymentRepository } from 'adapters/payments/persistence/inmemory/InMemoryMembershipFeeRepository';
|
import { InMemoryMembershipFeeRepository, InMemoryMemberPaymentRepository } from '/payments/persistence/inmemory/InMemoryMembershipFeeRepository';
|
||||||
import { InMemoryPrizeRepository } from 'adapters/payments/persistence/inmemory/InMemoryPrizeRepository';
|
import { InMemoryPrizeRepository } from '/payments/persistence/inmemory/InMemoryPrizeRepository';
|
||||||
import { InMemoryWalletRepository, InMemoryTransactionRepository } from 'adapters/payments/persistence/inmemory/InMemoryWalletRepository';
|
import { InMemoryWalletRepository, InMemoryTransactionRepository } from '/payments/persistence/inmemory/InMemoryWalletRepository';
|
||||||
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '/logging/ConsoleLogger';
|
||||||
|
|
||||||
// Repository injection tokens
|
// Repository injection tokens
|
||||||
export const PAYMENT_REPOSITORY_TOKEN = 'IPaymentRepository';
|
export const PAYMENT_REPOSITORY_TOKEN = 'IPaymentRepository';
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { IRaceRepository } from '@gridpilot/racing/domain/repositories/IRaceRepo
|
|||||||
import { ILeagueRepository } from '@gridpilot/racing/domain/repositories/ILeagueRepository';
|
import { ILeagueRepository } from '@gridpilot/racing/domain/repositories/ILeagueRepository';
|
||||||
|
|
||||||
// Import concrete in-memory implementations
|
// Import concrete in-memory implementations
|
||||||
import { InMemoryRaceRepository } from 'adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
import { InMemoryRaceRepository } from '/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||||
import { InMemoryLeagueRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
import { InMemoryLeagueRepository } from '/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||||
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '/logging/ConsoleLogger';
|
||||||
|
|
||||||
// Import use cases
|
// Import use cases
|
||||||
import { GetAllRacesUseCase } from '@gridpilot/racing/application/use-cases/GetAllRacesUseCase';
|
import { GetAllRacesUseCase } from '@gridpilot/racing/application/use-cases/GetAllRacesUseCase';
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ import { GetSponsorSponsorshipsUseCase } from '@gridpilot/racing/application/use
|
|||||||
import { GetEntitySponsorshipPricingUseCase } from '@gridpilot/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
|
import { GetEntitySponsorshipPricingUseCase } from '@gridpilot/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
|
||||||
|
|
||||||
// Import concrete in-memory implementations
|
// Import concrete in-memory implementations
|
||||||
import { InMemorySponsorRepository } from 'adapters/racing/persistence/inmemory/InMemorySponsorRepository';
|
import { InMemorySponsorRepository } from '/racing/persistence/inmemory/InMemorySponsorRepository';
|
||||||
import { InMemorySeasonSponsorshipRepository } from 'adapters/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository';
|
import { InMemorySeasonSponsorshipRepository } from '/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository';
|
||||||
import { InMemorySeasonRepository } from 'adapters/racing/persistence/inmemory/InMemorySeasonRepository';
|
import { InMemorySeasonRepository } from '/racing/persistence/inmemory/InMemorySeasonRepository';
|
||||||
import { InMemoryLeagueRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
import { InMemoryLeagueRepository } from '/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||||
import { InMemoryLeagueMembershipRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
|
import { InMemoryLeagueMembershipRepository } from '/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
|
||||||
import { InMemoryRaceRepository } from 'adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
import { InMemoryRaceRepository } from '/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||||
import { InMemorySponsorshipPricingRepository } from 'adapters/racing/persistence/inmemory/InMemorySponsorshipPricingRepository';
|
import { InMemorySponsorshipPricingRepository } from '/racing/persistence/inmemory/InMemorySponsorshipPricingRepository';
|
||||||
import { InMemorySponsorshipRequestRepository } from 'adapters/racing/persistence/inmemory/InMemorySponsorshipRequestRepository';
|
import { InMemorySponsorshipRequestRepository } from '/racing/persistence/inmemory/InMemorySponsorshipRequestRepository';
|
||||||
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '/logging/ConsoleLogger';
|
||||||
|
|
||||||
// Define injection tokens
|
// Define injection tokens
|
||||||
export const SPONSOR_REPOSITORY_TOKEN = 'ISponsorRepository';
|
export const SPONSOR_REPOSITORY_TOKEN = 'ISponsorRepository';
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import { IImageServicePort } from '@gridpilot/racing/application/ports/IImageSer
|
|||||||
import { Logger } from '@gridpilot/shared/application/Logger';
|
import { Logger } from '@gridpilot/shared/application/Logger';
|
||||||
|
|
||||||
// Import concrete in-memory implementations
|
// Import concrete in-memory implementations
|
||||||
import { InMemoryTeamRepository } from 'adapters/racing/persistence/inmemory/InMemoryTeamRepository';
|
import { InMemoryTeamRepository } from '/racing/persistence/inmemory/InMemoryTeamRepository';
|
||||||
import { InMemoryTeamMembershipRepository } from 'adapters/racing/persistence/inmemory/InMemoryTeamMembershipRepository';
|
import { InMemoryTeamMembershipRepository } from '/racing/persistence/inmemory/InMemoryTeamMembershipRepository';
|
||||||
import { InMemoryDriverRepository } from 'adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
import { InMemoryDriverRepository } from '/racing/persistence/inmemory/InMemoryDriverRepository';
|
||||||
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '/logging/ConsoleLogger';
|
||||||
|
|
||||||
// Import use cases
|
// Import use cases
|
||||||
import { GetAllTeamsUseCase } from '@gridpilot/racing/application/use-cases/GetAllTeamsUseCase';
|
import { GetAllTeamsUseCase } from '@gridpilot/racing/application/use-cases/GetAllTeamsUseCase';
|
||||||
|
|||||||
@@ -28,49 +28,49 @@
|
|||||||
"types": ["node", "express", "jest"],
|
"types": ["node", "express", "jest"],
|
||||||
"strictPropertyInitialization": false,
|
"strictPropertyInitialization": false,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@gridpilot/shared/*": [
|
"@core/shared/*": [
|
||||||
"../../core/shared/*"
|
"../../core/shared/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/shared/application/*": [
|
"@core/shared/application/*": [
|
||||||
"../../core/shared/application/*"
|
"../../core/shared/application/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/payments/*": [
|
"@core/payments/*": [
|
||||||
"../../core/payments/*"
|
"../../core/payments/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/payments/application/*": [
|
"@core/payments/application/*": [
|
||||||
"../../core/payments/application/*"
|
"../../core/payments/application/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/payments/domain/*": [
|
"@core/payments/domain/*": [
|
||||||
"../../core/payments/domain/*"
|
"../../core/payments/domain/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/racing/*": [
|
"@core/racing/*": [
|
||||||
"../../core/racing/*"
|
"../../core/racing/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/league/*": [
|
"@core/league/*": [
|
||||||
"../../core/league/*"
|
"../../core/league/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/analytics/*": [
|
"@core/analytics/*": [
|
||||||
"../../core/analytics/*"
|
"../../core/analytics/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/analytics/domain/repositories/*": [
|
"@core/analytics/domain/repositories/*": [
|
||||||
"../../core/analytics/domain/repositories/*"
|
"../../core/analytics/domain/repositories/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/analytics/domain/entities/*": [
|
"@core/analytics/domain/entities/*": [
|
||||||
"../../core/analytics/domain/entities/*"
|
"../../core/analytics/domain/entities/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/core/identity/domain/repositories/*": [
|
"@core/core/identity/domain/repositories/*": [
|
||||||
"../../core/identity/domain/repositories/*"
|
"../../core/identity/domain/repositories/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/core/identity/domain/services/*": [
|
"@core/core/identity/domain/services/*": [
|
||||||
"../../core/identity/domain/services/*"
|
"../../core/identity/domain/services/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/core/identity/domain/entities/*": [
|
"@core/core/identity/domain/entities/*": [
|
||||||
"../../core/identity/domain/entities/*"
|
"../../core/identity/domain/entities/*"
|
||||||
],
|
],
|
||||||
"@gridpilot/core/shared/logging/*": [
|
"@core/core/shared/logging/*": [
|
||||||
"../../core/shared/logging/*"
|
"../../core/shared/logging/*"
|
||||||
],
|
],
|
||||||
"adapters/*": [
|
"@adapters/*": [
|
||||||
"../../adapters/*"
|
"../../adapters/*"
|
||||||
],
|
],
|
||||||
"@nestjs/testing": [
|
"@nestjs/testing": [
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { InitiateLoginUseCase } from '@gridpilot/automation/application/use-case
|
|||||||
import { ClearSessionUseCase } from '@gridpilot/automation/application/use-cases/ClearSessionUseCase';
|
import { ClearSessionUseCase } from '@gridpilot/automation/application/use-cases/ClearSessionUseCase';
|
||||||
import { ConfirmCheckoutUseCase } from '@gridpilot/automation/application/use-cases/ConfirmCheckoutUseCase';
|
import { ConfirmCheckoutUseCase } from '@gridpilot/automation/application/use-cases/ConfirmCheckoutUseCase';
|
||||||
import { OverlaySyncService } from '@gridpilot/automation/application/services/OverlaySyncService';
|
import { OverlaySyncService } from '@gridpilot/automation/application/services/OverlaySyncService';
|
||||||
import type { IAutomationLifecycleEmitter } from '@gridpilot/automation/infrastructure/adapters/IAutomationLifecycleEmitter';
|
import type { IAutomationLifecycleEmitter } from '@gridpilot/automation/infrastructure//IAutomationLifecycleEmitter';
|
||||||
|
|
||||||
// Infrastructure
|
// Infrastructure
|
||||||
import { InMemorySessionRepository } from '@gridpilot/automation/infrastructure/repositories/InMemorySessionRepository';
|
import { InMemorySessionRepository } from '@gridpilot/automation/infrastructure/repositories/InMemorySessionRepository';
|
||||||
@@ -27,11 +27,11 @@ import {
|
|||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
AutomationAdapterMode,
|
AutomationAdapterMode,
|
||||||
FixtureServer,
|
FixtureServer,
|
||||||
} from '@gridpilot/automation/infrastructure/adapters/automation';
|
} from '@gridpilot/automation/infrastructure//automation';
|
||||||
import { MockAutomationEngineAdapter } from '@gridpilot/automation/infrastructure/adapters/automation/engine/MockAutomationEngineAdapter';
|
import { MockAutomationEngineAdapter } from '@gridpilot/automation/infrastructure//automation/engine/MockAutomationEngineAdapter';
|
||||||
import { AutomationEngineAdapter } from '@gridpilot/automation/infrastructure/adapters/automation/engine/AutomationEngineAdapter';
|
import { AutomationEngineAdapter } from '@gridpilot/automation/infrastructure//automation/engine/AutomationEngineAdapter';
|
||||||
import { ConsoleLogAdapter } from '@gridpilot/automation/infrastructure/adapters/logging/ConsoleLogAdapter';
|
import { ConsoleLogAdapter } from '@gridpilot/automation/infrastructure//logging/ConsoleLogAdapter';
|
||||||
import { NoOpLogAdapter } from '@gridpilot/automation/infrastructure/adapters/logging/NoOpLogAdapter';
|
import { NoOpLogAdapter } from '@gridpilot/automation/infrastructure//logging/NoOpLogAdapter';
|
||||||
import {
|
import {
|
||||||
loadAutomationConfig,
|
loadAutomationConfig,
|
||||||
getAutomationMode,
|
getAutomationMode,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { configureDIContainer, resetDIContainer, getDIContainer, resolveSessionDataPath, resolveTemplatePath } from './di-config';
|
import { configureDIContainer, resetDIContainer, getDIContainer, resolveSessionDataPath, resolveTemplatePath } from './di-config';
|
||||||
import { DI_TOKENS } from './di-tokens';
|
import { DI_TOKENS } from './di-tokens';
|
||||||
import { PlaywrightAutomationAdapter, FixtureServer } from '@gridpilot/automation/infrastructure/adapters/automation';
|
import { PlaywrightAutomationAdapter, FixtureServer } from '@gridpilot/automation/infrastructure//automation';
|
||||||
import { StartAutomationSessionUseCase } from '@gridpilot/automation/application/use-cases/StartAutomationSessionUseCase';
|
import { StartAutomationSessionUseCase } from '@gridpilot/automation/application/use-cases/StartAutomationSessionUseCase';
|
||||||
import { CheckAuthenticationUseCase } from '@gridpilot/automation/application/use-cases/CheckAuthenticationUseCase';
|
import { CheckAuthenticationUseCase } from '@gridpilot/automation/application/use-cases/CheckAuthenticationUseCase';
|
||||||
import { InitiateLoginUseCase } from '@gridpilot/automation/application/use-cases/InitiateLoginUseCase';
|
import { InitiateLoginUseCase } from '@gridpilot/automation/application/use-cases/InitiateLoginUseCase';
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { DIContainer } from './di-container';
|
|||||||
import type { HostedSessionConfig } from 'core/automation/domain/types/HostedSessionConfig';
|
import type { HostedSessionConfig } from 'core/automation/domain/types/HostedSessionConfig';
|
||||||
import { StepId } from 'core/automation/domain/value-objects/StepId';
|
import { StepId } from 'core/automation/domain/value-objects/StepId';
|
||||||
import { AuthenticationState } from 'core/automation/domain/value-objects/AuthenticationState';
|
import { AuthenticationState } from 'core/automation/domain/value-objects/AuthenticationState';
|
||||||
import { ElectronCheckoutConfirmationAdapter } from 'core/automation/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter';
|
import { ElectronCheckoutConfirmationAdapter } from 'core/automation/infrastructure//ipc/ElectronCheckoutConfirmationAdapter';
|
||||||
import type { OverlayAction } from 'core/automation/application/ports/OverlaySyncPort';
|
import type { OverlayAction } from 'core/automation/application/ports/OverlaySyncPort';
|
||||||
import type { IAutomationLifecycleEmitter } from 'core/automation/infrastructure/adapters/IAutomationLifecycleEmitter';
|
import type { IAutomationLifecycleEmitter } from 'core/automation/infrastructure//IAutomationLifecycleEmitter';
|
||||||
|
|
||||||
let progressMonitorInterval: NodeJS.Timeout | null = null;
|
let progressMonitorInterval: NodeJS.Timeout | null = null;
|
||||||
let lifecycleSubscribed = false;
|
let lifecycleSubscribed = false;
|
||||||
@@ -335,7 +335,7 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
|
|||||||
loader.setDevelopmentMode(mode);
|
loader.setDevelopmentMode(mode);
|
||||||
// Ensure runtime automation wiring reflects the new browser mode
|
// Ensure runtime automation wiring reflects the new browser mode
|
||||||
if ('refreshBrowserAutomation' in container) {
|
if ('refreshBrowserAutomation' in container) {
|
||||||
// Call method to refresh adapters/use-cases that depend on browser mode
|
// Call method to refresh /use-cases that depend on browser mode
|
||||||
container.refreshBrowserAutomation();
|
container.refreshBrowserAutomation();
|
||||||
}
|
}
|
||||||
logger.info('Browser mode updated', { mode });
|
logger.info('Browser mode updated', { mode });
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
{
|
{
|
||||||
"extends": "next/core-web-vitals",
|
"extends": "next/core-web-vitals",
|
||||||
|
"plugins": ["boundaries"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"react/no-unescaped-entities": "off",
|
"react/no-unescaped-entities": "off",
|
||||||
"@next/next/no-img-element": "warn",
|
"@next/next/no-img-element": "warn",
|
||||||
"react-hooks/exhaustive-deps": "warn",
|
"react-hooks/exhaustive-deps": "warn",
|
||||||
"@typescript-eslint/no-explicit-any": "error"
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
|
"boundaries/element-types": [
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
"default": "disallow",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"from": ["website"],
|
||||||
|
"allow": ["website"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,20 +18,20 @@
|
|||||||
"@/lib/*": ["./lib/*"],
|
"@/lib/*": ["./lib/*"],
|
||||||
"@/components/*": ["./components/*"],
|
"@/components/*": ["./components/*"],
|
||||||
"@/app/*": ["./app/*"],
|
"@/app/*": ["./app/*"],
|
||||||
"@gridpilot/identity": ["../../core/identity/index.ts"],
|
"@core/identity": ["../../core/identity/index.ts"],
|
||||||
"@gridpilot/identity/*": ["../../core/identity/*"],
|
"@core/identity/*": ["../../core/identity/*"],
|
||||||
"@gridpilot/racing": ["../../core/racing/index.ts"],
|
"@core/racing": ["../../core/racing/index.ts"],
|
||||||
"@gridpilot/racing/*": ["../../core/racing/*"],
|
"@core/racing/*": ["../../core/racing/*"],
|
||||||
"@gridpilot/social": ["../../core/social/index.ts"],
|
"@core/social": ["../../core/social/index.ts"],
|
||||||
"@gridpilot/social/*": ["../../core/social/*"],
|
"@core/social/*": ["../../core/social/*"],
|
||||||
"@gridpilot/testing-support": ["../../core/testing-support/index.ts"],
|
"@core/testing-support": ["../../core/testing-support/index.ts"],
|
||||||
"@gridpilot/testing-support/*": ["../../core/testing-support/*"],
|
"@core/testing-support/*": ["../../core/testing-support/*"],
|
||||||
"@gridpilot/media": ["../../core/media/index.ts"],
|
"@core/media": ["../../core/media/index.ts"],
|
||||||
"@gridpilot/media/*": ["../../core/media/*"],
|
"@core/media/*": ["../../core/media/*"],
|
||||||
"@gridpilot/shared/logging": ["../../core/shared/logging"],
|
"@core/shared/logging": ["../../core/shared/logging"],
|
||||||
"@gridpilot/shared/*": ["../../core/shared/*"],
|
"@core/shared/*": ["../../core/shared/*"],
|
||||||
"@gridpilot/core/*": ["../../core/*"],
|
"@core/core/*": ["../../core/*"],
|
||||||
"@gridpilot/api/*": ["../../apps/api/src/*"]
|
"@core/api/*": ["../../apps/api/src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ export { InMemoryNotificationRepository } from './repositories/InMemoryNotificat
|
|||||||
export { InMemoryNotificationPreferenceRepository } from './repositories/InMemoryNotificationPreferenceRepository';
|
export { InMemoryNotificationPreferenceRepository } from './repositories/InMemoryNotificationPreferenceRepository';
|
||||||
|
|
||||||
// Adapters
|
// Adapters
|
||||||
export { InAppNotificationAdapter } from './adapters/InAppNotificationAdapter';
|
export { InAppNotificationAdapter } from './/InAppNotificationAdapter';
|
||||||
export { NotificationGatewayRegistry } from './adapters/NotificationGatewayRegistry';
|
export { NotificationGatewayRegistry } from './/NotificationGatewayRegistry';
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, expect, test } from 'vitest'
|
import { describe, expect, test } from 'vitest'
|
||||||
import { OverlayAction, ActionAck } from '../../../../core/automation/application/ports/IOverlaySyncPort'
|
import { OverlayAction, ActionAck } from '../../../../core/automation/application/ports/IOverlaySyncPort'
|
||||||
import { IAutomationEventPublisher, AutomationEvent } from '../../../../core/automation/application/ports/IAutomationEventPublisher'
|
import { IAutomationEventPublisher, AutomationEvent } from '../../../../core/automation/application/ports/IAutomationEventPublisher'
|
||||||
import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure/adapters/IAutomationLifecycleEmitter'
|
import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure//IAutomationLifecycleEmitter'
|
||||||
import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService'
|
import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService'
|
||||||
|
|
||||||
class MockLifecycleEmitter implements IAutomationLifecycleEmitter {
|
class MockLifecycleEmitter implements IAutomationLifecycleEmitter {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from 'vitest'
|
import { describe, expect, test } from 'vitest'
|
||||||
import { OverlayAction } from '../../../../core/automation/application/ports/OverlaySyncPort'
|
import { OverlayAction } from '../../../../core/automation/application/ports/OverlaySyncPort'
|
||||||
import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure/adapters/IAutomationLifecycleEmitter'
|
import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure//IAutomationLifecycleEmitter'
|
||||||
import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService'
|
import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService'
|
||||||
|
|
||||||
class MockLifecycleEmitter implements IAutomationLifecycleEmitter {
|
class MockLifecycleEmitter implements IAutomationLifecycleEmitter {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
|||||||
import {
|
import {
|
||||||
FixtureServer,
|
FixtureServer,
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
|
|
||||||
describe('Real Playwright hosted-session smoke (fixtures, steps 2–7)', () => {
|
describe('Real Playwright hosted-session smoke (fixtures, steps 2–7)', () => {
|
||||||
let server: FixtureServer;
|
let server: FixtureServer;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|||||||
import { DIContainer } from '../../../apps/companion/main/di-container';
|
import { DIContainer } from '../../../apps/companion/main/di-container';
|
||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
||||||
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation';
|
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
|
||||||
|
|
||||||
describe('Companion UI - hosted workflow via fixture-backed real stack', () => {
|
describe('Companion UI - hosted workflow via fixture-backed real stack', () => {
|
||||||
let container: DIContainer;
|
let container: DIContainer;
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import {
|
import {
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import {
|
import {
|
||||||
IRACING_SELECTORS,
|
IRACING_SELECTORS,
|
||||||
IRACING_TIMEOUTS,
|
IRACING_TIMEOUTS,
|
||||||
} from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
} from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
|
|
||||||
const shouldRun = process.env.HOSTED_REAL_E2E === '1';
|
const shouldRun = process.env.HOSTED_REAL_E2E === '1';
|
||||||
const describeMaybe = shouldRun ? describe : describe.skip;
|
const describeMaybe = shouldRun ? describe : describe.skip;
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import {
|
import {
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import {
|
import {
|
||||||
IRACING_SELECTORS,
|
IRACING_SELECTORS,
|
||||||
IRACING_TIMEOUTS,
|
IRACING_TIMEOUTS,
|
||||||
} from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
} from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
|
|
||||||
const shouldRun = process.env.HOSTED_REAL_E2E === '1';
|
const shouldRun = process.env.HOSTED_REAL_E2E === '1';
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import path from 'path';
|
|||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import {
|
import {
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import {
|
import {
|
||||||
IRACING_SELECTORS,
|
IRACING_SELECTORS,
|
||||||
IRACING_TIMEOUTS,
|
IRACING_TIMEOUTS,
|
||||||
} from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
} from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
|
|
||||||
const shouldRun = process.env.HOSTED_REAL_E2E === '1';
|
const shouldRun = process.env.HOSTED_REAL_E2E === '1';
|
||||||
const describeMaybe = shouldRun ? describe : describe.skip;
|
const describeMaybe = shouldRun ? describe : describe.skip;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 2 – create race', () => {
|
describe('Step 2 – create race', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 3 – race information', () => {
|
describe('Step 3 – race information', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 4 – server details', () => {
|
describe('Step 4 – server details', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 5 – set admins', () => {
|
describe('Step 5 – set admins', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 6 – admins', () => {
|
describe('Step 6 – admins', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 7 – time limits', () => {
|
describe('Step 7 – time limits', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 8 – cars', () => {
|
describe('Step 8 – cars', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 13 – track options', () => {
|
describe('Step 13 – track options', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 14 – time of day', () => {
|
describe('Step 14 – time of day', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
import type { StepHarness } from '../support/StepHarness';
|
import type { StepHarness } from '../support/StepHarness';
|
||||||
import { createStepHarness } from '../support/StepHarness';
|
import { createStepHarness } from '../support/StepHarness';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
|
|
||||||
describe('Step 15 – weather', () => {
|
describe('Step 15 – weather', () => {
|
||||||
let harness: StepHarness;
|
let harness: StepHarness;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import type { PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation';
|
import type { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
|
||||||
import type { AutomationResult } from 'core/automation/application/ports/AutomationResults';
|
import type { AutomationResult } from 'core/automation/application/ports/AutomationResults';
|
||||||
|
|
||||||
export function assertAutoNavigationConfig(config: Record<string, unknown>): void {
|
export function assertAutoNavigationConfig(config: Record<string, unknown>): void {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
|||||||
import {
|
import {
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
FixtureServer,
|
FixtureServer,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
|
|
||||||
export interface StepHarness {
|
export interface StepHarness {
|
||||||
server: FixtureServer;
|
server: FixtureServer;
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
FixtureServer,
|
FixtureServer,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
import { executeStepWithAutoNavigationGuard } from '../support/AutoNavGuard';
|
import { executeStepWithAutoNavigationGuard } from '../support/AutoNavGuard';
|
||||||
|
|
||||||
describe('Hosted validator guards (fixture-backed, real stack)', () => {
|
describe('Hosted validator guards (fixture-backed, real stack)', () => {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
FixtureServer,
|
FixtureServer,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
import { executeStepWithAutoNavigationGuard } from '../support/AutoNavGuard';
|
import { executeStepWithAutoNavigationGuard } from '../support/AutoNavGuard';
|
||||||
|
|
||||||
describe('Workflow – hosted session autonav slice (fixture-backed, real stack)', () => {
|
describe('Workflow – hosted session autonav slice (fixture-backed, real stack)', () => {
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
FixtureServer,
|
FixtureServer,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import { InMemorySessionRepository } from 'core/automation/infrastructure/repositories/InMemorySessionRepository';
|
import { InMemorySessionRepository } from 'core/automation/infrastructure/repositories/InMemorySessionRepository';
|
||||||
import { AutomationEngineAdapter } from 'core/automation/infrastructure/adapters/automation/engine/AutomationEngineAdapter';
|
import { AutomationEngineAdapter } from 'core/automation/infrastructure//automation/engine/AutomationEngineAdapter';
|
||||||
import { StartAutomationSessionUseCase } from 'core/automation/application/use-cases/StartAutomationSessionUseCase';
|
import { StartAutomationSessionUseCase } from 'core/automation/application/use-cases/StartAutomationSessionUseCase';
|
||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
|
|
||||||
describe('Workflow – hosted session end-to-end (fixture-backed, real stack)', () => {
|
describe('Workflow – hosted session end-to-end (fixture-backed, real stack)', () => {
|
||||||
let server: FixtureServer;
|
let server: FixtureServer;
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
PlaywrightAutomationAdapter,
|
PlaywrightAutomationAdapter,
|
||||||
FixtureServer,
|
FixtureServer,
|
||||||
} from 'core/automation/infrastructure/adapters/automation';
|
} from 'core/automation/infrastructure//automation';
|
||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors';
|
import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
|
||||||
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter';
|
import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
|
||||||
|
|
||||||
describe('Workflow – steps 7–9 cars flow (fixture-backed, real stack)', () => {
|
describe('Workflow – steps 7–9 cars flow (fixture-backed, real stack)', () => {
|
||||||
let adapter: PlaywrightAutomationAdapter;
|
let adapter: PlaywrightAutomationAdapter;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, test, expect, beforeEach, vi } from 'vitest';
|
import { describe, test, expect, beforeEach, vi } from 'vitest';
|
||||||
import type { Page } from 'playwright';
|
import type { Page } from 'playwright';
|
||||||
import { AuthenticationGuard } from '@gridpilot/automation/infrastructure/adapters/automation/auth/AuthenticationGuard';
|
import { AuthenticationGuard } from '@gridpilot/automation/infrastructure//automation/auth/AuthenticationGuard';
|
||||||
|
|
||||||
describe('AuthenticationGuard', () => {
|
describe('AuthenticationGuard', () => {
|
||||||
let mockPage: Page;
|
let mockPage: Page;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ vi.mock('electron', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { ElectronCheckoutConfirmationAdapter } from '@gridpilot/automation/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter';
|
import { ElectronCheckoutConfirmationAdapter } from '@gridpilot/automation/infrastructure//ipc/ElectronCheckoutConfirmationAdapter';
|
||||||
import { CheckoutPrice } from '@gridpilot/automation/domain/value-objects/CheckoutPrice';
|
import { CheckoutPrice } from '@gridpilot/automation/domain/value-objects/CheckoutPrice';
|
||||||
import { CheckoutState } from '@gridpilot/automation/domain/value-objects/CheckoutState';
|
import { CheckoutState } from '@gridpilot/automation/domain/value-objects/CheckoutState';
|
||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
import type { Page, BrowserContext } from 'playwright';
|
import type { Page, BrowserContext } from 'playwright';
|
||||||
import { PlaywrightAuthSessionService } from '../../../../core/automation/infrastructure/adapters/automation/auth/PlaywrightAuthSessionService';
|
import { PlaywrightAuthSessionService } from '../../../../core/automation/infrastructure//automation/auth/PlaywrightAuthSessionService';
|
||||||
import type { PlaywrightBrowserSession } from '../../../../core/automation/infrastructure/adapters/automation/core/PlaywrightBrowserSession';
|
import type { PlaywrightBrowserSession } from '../../../../core/automation/infrastructure//automation/core/PlaywrightBrowserSession';
|
||||||
import type { SessionCookieStore } from '../../../../core/automation/infrastructure/adapters/automation/auth/SessionCookieStore';
|
import type { SessionCookieStore } from '../../../../core/automation/infrastructure//automation/auth/SessionCookieStore';
|
||||||
import type { IPlaywrightAuthFlow } from '../../../../core/automation/infrastructure/adapters/automation/auth/PlaywrightAuthFlow';
|
import type { IPlaywrightAuthFlow } from '../../../../core/automation/infrastructure//automation/auth/PlaywrightAuthFlow';
|
||||||
import type { LoggerPort as Logger } from '../../../../core/automation/application/ports/LoggerPort';
|
import type { LoggerPort as Logger } from '../../../../core/automation/application/ports/LoggerPort';
|
||||||
import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState';
|
import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState';
|
||||||
import { Result } from '../../../../core/shared/result/Result';
|
import { Result } from '../../../../core/shared/result/Result';
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { describe, it, expect, vi } from 'vitest';
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
import type { Page, Locator } from 'playwright';
|
import type { Page, Locator } from 'playwright';
|
||||||
import { PlaywrightAuthSessionService } from '../../../../core/automation/infrastructure/adapters/automation/auth/PlaywrightAuthSessionService';
|
import { PlaywrightAuthSessionService } from '../../../../core/automation/infrastructure//automation/auth/PlaywrightAuthSessionService';
|
||||||
import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState';
|
import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState';
|
||||||
import { BrowserAuthenticationState } from '@gridpilot/automation/domain/value-objects/BrowserAuthenticationState';
|
import { BrowserAuthenticationState } from '@gridpilot/automation/domain/value-objects/BrowserAuthenticationState';
|
||||||
import type { LoggerPort as Logger } from '../../../../core/automation/application/ports/LoggerPort';
|
import type { LoggerPort as Logger } from '../../../../core/automation/application/ports/LoggerPort';
|
||||||
import type { Result } from '../../../../core/shared/result/Result';
|
import type { Result } from '../../../../core/shared/result/Result';
|
||||||
import type { PlaywrightBrowserSession } from '../../../../core/automation/infrastructure/adapters/automation/core/PlaywrightBrowserSession';
|
import type { PlaywrightBrowserSession } from '../../../../core/automation/infrastructure//automation/core/PlaywrightBrowserSession';
|
||||||
import type { SessionCookieStore } from '../../../../core/automation/infrastructure/adapters/automation/auth/SessionCookieStore';
|
import type { SessionCookieStore } from '../../../../core/automation/infrastructure//automation/auth/SessionCookieStore';
|
||||||
import type { IPlaywrightAuthFlow } from '../../../../core/automation/infrastructure/adapters/automation/auth/PlaywrightAuthFlow';
|
import type { IPlaywrightAuthFlow } from '../../../../core/automation/infrastructure//automation/auth/PlaywrightAuthFlow';
|
||||||
|
|
||||||
describe('PlaywrightAuthSessionService.verifyPageAuthentication', () => {
|
describe('PlaywrightAuthSessionService.verifyPageAuthentication', () => {
|
||||||
function createService(deps: {
|
function createService(deps: {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, test, expect, beforeEach } from 'vitest';
|
import { describe, test, expect, beforeEach } from 'vitest';
|
||||||
import { SessionCookieStore } from '@gridpilot/automation/infrastructure/adapters/automation/auth/SessionCookieStore';
|
import { SessionCookieStore } from '@gridpilot/automation/infrastructure//automation/auth/SessionCookieStore';
|
||||||
import type { Cookie } from 'playwright';
|
import type { Cookie } from 'playwright';
|
||||||
|
|
||||||
const logger = console as any;
|
const logger = console as any;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { configureDIContainer, resetDIContainer } from "../../../apps/companion/
|
|||||||
import { DI_TOKENS } from "../../../apps/companion/main/di-tokens";
|
import { DI_TOKENS } from "../../../apps/companion/main/di-tokens";
|
||||||
import { OverlaySyncService } from "@gridpilot/automation/application/services/OverlaySyncService";
|
import { OverlaySyncService } from "@gridpilot/automation/application/services/OverlaySyncService";
|
||||||
import { LoggerPort } from "@gridpilot/automation/application/ports/LoggerPort";
|
import { LoggerPort } from "@gridpilot/automation/application/ports/LoggerPort";
|
||||||
import { IAutomationLifecycleEmitter, LifecycleCallback } from "@gridpilot/automation/infrastructure/adapters/IAutomationLifecycleEmitter";
|
import { IAutomationLifecycleEmitter, LifecycleCallback } from "@gridpilot/automation/infrastructure//IAutomationLifecycleEmitter";
|
||||||
import { AutomationEventPublisherPort, AutomationEvent } from "@gridpilot/automation/application/ports/AutomationEventPublisherPort";
|
import { AutomationEventPublisherPort, AutomationEvent } from "@gridpilot/automation/application/ports/AutomationEventPublisherPort";
|
||||||
import { ConsoleLogAdapter } from "@gridpilot/automation/infrastructure/adapters/logging/ConsoleLogAdapter";
|
import { ConsoleLogAdapter } from "@gridpilot/automation/infrastructure//logging/ConsoleLogAdapter";
|
||||||
import { describe, it, expect, beforeEach, afterEach, vi, SpyInstance } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach, vi, SpyInstance } from 'vitest';
|
||||||
|
|
||||||
describe("OverlaySyncService Integration with ConsoleLogAdapter", () => {
|
describe("OverlaySyncService Integration with ConsoleLogAdapter", () => {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { PlaywrightAutomationAdapter } = await import(
|
const { PlaywrightAutomationAdapter } = await import(
|
||||||
'core/automation/infrastructure/adapters/automation'
|
'core/automation/infrastructure//automation'
|
||||||
);
|
);
|
||||||
|
|
||||||
adapter = new PlaywrightAutomationAdapter({
|
adapter = new PlaywrightAutomationAdapter({
|
||||||
@@ -111,7 +111,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { PlaywrightAutomationAdapter } = await import(
|
const { PlaywrightAutomationAdapter } = await import(
|
||||||
'core/automation/infrastructure/adapters/automation'
|
'core/automation/infrastructure//automation'
|
||||||
);
|
);
|
||||||
|
|
||||||
adapter = new PlaywrightAutomationAdapter({
|
adapter = new PlaywrightAutomationAdapter({
|
||||||
@@ -135,7 +135,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { PlaywrightAutomationAdapter } = await import(
|
const { PlaywrightAutomationAdapter } = await import(
|
||||||
'core/automation/infrastructure/adapters/automation'
|
'core/automation/infrastructure//automation'
|
||||||
);
|
);
|
||||||
|
|
||||||
adapter = new PlaywrightAutomationAdapter({
|
adapter = new PlaywrightAutomationAdapter({
|
||||||
@@ -164,7 +164,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { PlaywrightAutomationAdapter } = await import(
|
const { PlaywrightAutomationAdapter } = await import(
|
||||||
'core/automation/infrastructure/adapters/automation'
|
'core/automation/infrastructure//automation'
|
||||||
);
|
);
|
||||||
|
|
||||||
adapter = new PlaywrightAutomationAdapter({
|
adapter = new PlaywrightAutomationAdapter({
|
||||||
@@ -185,7 +185,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { PlaywrightAutomationAdapter } = await import(
|
const { PlaywrightAutomationAdapter } = await import(
|
||||||
'core/automation/infrastructure/adapters/automation'
|
'core/automation/infrastructure//automation'
|
||||||
);
|
);
|
||||||
|
|
||||||
adapter = new PlaywrightAutomationAdapter({
|
adapter = new PlaywrightAutomationAdapter({
|
||||||
@@ -228,7 +228,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { PlaywrightAutomationAdapter } = await import(
|
const { PlaywrightAutomationAdapter } = await import(
|
||||||
'core/automation/infrastructure/adapters/automation'
|
'core/automation/infrastructure//automation'
|
||||||
);
|
);
|
||||||
|
|
||||||
adapter = new PlaywrightAutomationAdapter(
|
adapter = new PlaywrightAutomationAdapter(
|
||||||
@@ -259,7 +259,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { PlaywrightAutomationAdapter } = await import(
|
const { PlaywrightAutomationAdapter } = await import(
|
||||||
'core/automation/infrastructure/adapters/automation'
|
'core/automation/infrastructure//automation'
|
||||||
);
|
);
|
||||||
|
|
||||||
const userDataDir = path.join(process.cwd(), 'test-browser-data');
|
const userDataDir = path.join(process.cwd(), 'test-browser-data');
|
||||||
@@ -290,7 +290,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
|
|||||||
configurable: true
|
configurable: true
|
||||||
});
|
});
|
||||||
const { PlaywrightAutomationAdapter } = await import(
|
const { PlaywrightAutomationAdapter } = await import(
|
||||||
'core/automation/infrastructure/adapters/automation'
|
'core/automation/infrastructure//automation'
|
||||||
);
|
);
|
||||||
const { BrowserModeConfigLoader } = await import(
|
const { BrowserModeConfigLoader } = await import(
|
||||||
'../../../core/automation/infrastructure/config/BrowserModeConfig'
|
'../../../core/automation/infrastructure/config/BrowserModeConfig'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||||
import { Result } from '../../../core/shared/result/Result';
|
import { Result } from '../../../core/shared/result/Result';
|
||||||
import { CheckoutPriceExtractor } from '../../../core/automation/infrastructure/adapters/automation/CheckoutPriceExtractor';
|
import { CheckoutPriceExtractor } from '../../../core/automation/infrastructure//automation/CheckoutPriceExtractor';
|
||||||
import { CheckoutStateEnum } from '@gridpilot/automation/domain/value-objects/CheckoutState';
|
import { CheckoutStateEnum } from '@gridpilot/automation/domain/value-objects/CheckoutState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||||
import { FixtureServer, getAllStepFixtureMappings, PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation';
|
import { FixtureServer, getAllStepFixtureMappings, PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
|
||||||
|
|
||||||
declare const getComputedStyle: any;
|
declare const getComputedStyle: any;
|
||||||
declare const document: any;
|
declare const document: any;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||||
import { MockBrowserAutomationAdapter } from 'core/automation/infrastructure/adapters/automation';
|
import { MockBrowserAutomationAdapter } from 'core/automation/infrastructure//automation';
|
||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
|
|
||||||
describe('MockBrowserAutomationAdapter Integration Tests', () => {
|
describe('MockBrowserAutomationAdapter Integration Tests', () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, test, expect } from 'vitest'
|
import { describe, test, expect } from 'vitest'
|
||||||
import type { Page } from 'playwright'
|
import type { Page } from 'playwright'
|
||||||
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation'
|
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation'
|
||||||
|
|
||||||
describe('CarsFlow integration', () => {
|
describe('CarsFlow integration', () => {
|
||||||
test('adapter emits panel-attached then action-started then action-complete for performAddCar', async () => {
|
test('adapter emits panel-attached then action-started then action-complete for performAddCar', async () => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { AutomationEvent } from 'core/automation/application/ports/IAutomat
|
|||||||
import type {
|
import type {
|
||||||
IAutomationLifecycleEmitter,
|
IAutomationLifecycleEmitter,
|
||||||
LifecycleCallback,
|
LifecycleCallback,
|
||||||
} from 'core/automation/infrastructure/adapters/IAutomationLifecycleEmitter';
|
} from 'core/automation/infrastructure//IAutomationLifecycleEmitter';
|
||||||
import type {
|
import type {
|
||||||
OverlayAction,
|
OverlayAction,
|
||||||
ActionAck,
|
ActionAck,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|||||||
import { DIContainer } from '../../../..//apps/companion/main/di-container';
|
import { DIContainer } from '../../../..//apps/companion/main/di-container';
|
||||||
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure/adapters/automation';
|
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure//automation';
|
||||||
|
|
||||||
describe('companion start automation - browser mode refresh wiring', () => {
|
describe('companion start automation - browser mode refresh wiring', () => {
|
||||||
const originalEnv = { ...process.env };
|
const originalEnv = { ...process.env };
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|||||||
import { DIContainer } from '../../../..//apps/companion/main/di-container';
|
import { DIContainer } from '../../../..//apps/companion/main/di-container';
|
||||||
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
||||||
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
||||||
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure/adapters/automation';
|
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure//automation';
|
||||||
|
|
||||||
describe('companion start automation - browser not connected at step 1', () => {
|
describe('companion start automation - browser not connected at step 1', () => {
|
||||||
const originalEnv = { ...process.env };
|
const originalEnv = { ...process.env };
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||||
import { DIContainer } from '../../../..//apps/companion/main/di-container';
|
import { DIContainer } from '../../../..//apps/companion/main/di-container';
|
||||||
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
||||||
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure/adapters/automation';
|
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure//automation';
|
||||||
|
|
||||||
describe('companion start automation - browser connection failure before steps', () => {
|
describe('companion start automation - browser connection failure before steps', () => {
|
||||||
const originalEnv = { ...process.env };
|
const originalEnv = { ...process.env };
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import { CheckAuthenticationUseCase } from '../../core/automation/application/us
|
|||||||
import { InitiateLoginUseCase } from '../../core/automation/application/use-cases/InitiateLoginUseCase';
|
import { InitiateLoginUseCase } from '../../core/automation/application/use-cases/InitiateLoginUseCase';
|
||||||
import { ClearSessionUseCase } from '../../core/automation/application/use-cases/ClearSessionUseCase';
|
import { ClearSessionUseCase } from '../../core/automation/application/use-cases/ClearSessionUseCase';
|
||||||
import { ConfirmCheckoutUseCase } from '../../core/automation/application/use-cases/ConfirmCheckoutUseCase';
|
import { ConfirmCheckoutUseCase } from '../../core/automation/application/use-cases/ConfirmCheckoutUseCase';
|
||||||
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation';
|
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
|
||||||
import { InMemorySessionRepository } from '../../core/automation/infrastructure/repositories/InMemorySessionRepository';
|
import { InMemorySessionRepository } from '../../core/automation/infrastructure/repositories/InMemorySessionRepository';
|
||||||
import { NoOpLogAdapter } from '../../core/automation/infrastructure/adapters/logging/NoOpLogAdapter';
|
import { NoOpLogAdapter } from '../../core/automation/infrastructure//logging/NoOpLogAdapter';
|
||||||
|
|
||||||
// Mock Electron's app module
|
// Mock Electron's app module
|
||||||
vi.mock('electron', () => ({
|
vi.mock('electron', () => ({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect, afterEach, beforeAll, afterAll } from 'vitest';
|
import { describe, it, expect, afterEach, beforeAll, afterAll } from 'vitest';
|
||||||
import { PlaywrightAutomationAdapter, FixtureServer } from 'core/automation/infrastructure/adapters/automation';
|
import { PlaywrightAutomationAdapter, FixtureServer } from 'core/automation/infrastructure//automation';
|
||||||
import { NoOpLogAdapter } from '../../core/automation/infrastructure/adapters/logging/NoOpLogAdapter';
|
import { NoOpLogAdapter } from '../../core/automation/infrastructure//logging/NoOpLogAdapter';
|
||||||
|
|
||||||
describe('Playwright Adapter Smoke Tests', () => {
|
describe('Playwright Adapter Smoke Tests', () => {
|
||||||
let adapter: PlaywrightAutomationAdapter | undefined;
|
let adapter: PlaywrightAutomationAdapter | undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user