test(erp): add unit tests for ERP services and test coverage docs
Add unit tests for core ERP service modules including ErpBrowserManager, cleaner, erp-auth, extractor-core, extractor, and order-resolver. Also includes test coverage improvement plan and quality review report. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
216
tests/unit/services/erp/erp-auth.test.ts
Normal file
216
tests/unit/services/erp/erp-auth.test.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { chromium } from 'playwright'
|
||||
import { ErpAuthService } from '@main/services/erp/erp-auth'
|
||||
import type { ErpConfig } from '@main/types/erp.types'
|
||||
|
||||
// Mock logger
|
||||
vi.mock('@main/services/logger', () => ({
|
||||
createLogger: vi.fn(() => ({
|
||||
info: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn()
|
||||
}))
|
||||
}))
|
||||
|
||||
// Mock playwright
|
||||
vi.mock('playwright', () => ({
|
||||
chromium: {
|
||||
launch: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@main/services/erp/erp-error-context', () => ({
|
||||
capturePageContext: vi.fn().mockResolvedValue({})
|
||||
}))
|
||||
|
||||
vi.mock('@main/services/erp/page-diagnostics', () => ({
|
||||
attachPageDiagnostics: vi.fn(),
|
||||
attachContextDiagnostics: vi.fn()
|
||||
}))
|
||||
|
||||
describe('ErpAuthService', () => {
|
||||
let config: ErpConfig
|
||||
let service: ErpAuthService
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
config = {
|
||||
url: 'https://test-erp.com',
|
||||
username: 'testuser',
|
||||
password: 'testpass',
|
||||
headless: true
|
||||
}
|
||||
})
|
||||
|
||||
describe('constructor()', () => {
|
||||
it('should store config', () => {
|
||||
service = new ErpAuthService(config)
|
||||
|
||||
// Verify config is stored by checking isActive returns false (default state)
|
||||
expect(service.isActive()).toBe(false)
|
||||
})
|
||||
|
||||
it('should initialize with null session', () => {
|
||||
service = new ErpAuthService(config)
|
||||
|
||||
expect(service.isActive()).toBe(false)
|
||||
expect(() => service.getSession()).toThrow('Not logged in')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getSession()', () => {
|
||||
beforeEach(() => {
|
||||
service = new ErpAuthService(config)
|
||||
})
|
||||
|
||||
it('should throw error when not logged in', () => {
|
||||
expect(() => service.getSession()).toThrow('Not logged in. Call login() first.')
|
||||
})
|
||||
|
||||
it('should return session when logged in', () => {
|
||||
// Manually set session state (bypassing login for unit test)
|
||||
;(service as any).session = {
|
||||
browser: {},
|
||||
context: {},
|
||||
page: {},
|
||||
mainFrame: {},
|
||||
isLoggedIn: true
|
||||
}
|
||||
|
||||
const session = service.getSession()
|
||||
|
||||
expect(session).toBeDefined()
|
||||
expect(session.isLoggedIn).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isActive()', () => {
|
||||
beforeEach(() => {
|
||||
service = new ErpAuthService(config)
|
||||
})
|
||||
|
||||
it('should return false when not logged in', () => {
|
||||
expect(service.isActive()).toBe(false)
|
||||
})
|
||||
|
||||
it('should return true when logged in', () => {
|
||||
// Manually set session state (bypassing login for unit test)
|
||||
;(service as any).session = {
|
||||
browser: {},
|
||||
context: {},
|
||||
page: {},
|
||||
mainFrame: {},
|
||||
isLoggedIn: true
|
||||
}
|
||||
|
||||
expect(service.isActive()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('login()', () => {
|
||||
let mockBrowser: any
|
||||
let mockContext: any
|
||||
let mockPage: any
|
||||
let mockFrame: any
|
||||
|
||||
beforeEach(() => {
|
||||
mockFrame = {
|
||||
locator: vi.fn().mockReturnThis(),
|
||||
getByRole: vi.fn().mockReturnValue({
|
||||
fill: vi.fn().mockResolvedValue(undefined),
|
||||
click: vi.fn().mockResolvedValue(undefined)
|
||||
}),
|
||||
getByText: vi.fn().mockReturnValue({
|
||||
waitFor: vi.fn().mockResolvedValue(undefined),
|
||||
isVisible: vi.fn().mockResolvedValue(false)
|
||||
}),
|
||||
waitFor: vi.fn().mockResolvedValue(undefined)
|
||||
}
|
||||
|
||||
mockPage = {
|
||||
goto: vi.fn().mockResolvedValue(undefined),
|
||||
waitForLoadState: vi.fn().mockResolvedValue(undefined),
|
||||
waitForSelector: vi.fn().mockResolvedValue(undefined),
|
||||
locator: vi.fn().mockReturnValue({
|
||||
contentFrame: vi.fn().mockResolvedValue(mockFrame)
|
||||
})
|
||||
}
|
||||
|
||||
mockContext = {
|
||||
newPage: vi.fn().mockResolvedValue(mockPage),
|
||||
close: vi.fn().mockResolvedValue(undefined)
|
||||
}
|
||||
|
||||
mockBrowser = {
|
||||
newContext: vi.fn().mockResolvedValue(mockContext),
|
||||
close: vi.fn().mockResolvedValue(undefined)
|
||||
}
|
||||
|
||||
vi.mocked(chromium.launch).mockResolvedValue(mockBrowser as any)
|
||||
})
|
||||
|
||||
it('should create session on successful login', async () => {
|
||||
service = new ErpAuthService(config)
|
||||
|
||||
const session = await service.login()
|
||||
|
||||
expect(chromium.launch).toHaveBeenCalledWith(expect.objectContaining({ headless: true }))
|
||||
expect(mockBrowser.newContext).toHaveBeenCalled()
|
||||
expect(mockContext.newPage).toHaveBeenCalled()
|
||||
expect(session.isLoggedIn).toBe(true)
|
||||
expect(session.browser).toBe(mockBrowser)
|
||||
expect(session.context).toBe(mockContext)
|
||||
expect(session.page).toBe(mockPage)
|
||||
expect(session.mainFrame).toBe(mockFrame)
|
||||
})
|
||||
|
||||
it('should reuse existing session if already logged in', async () => {
|
||||
service = new ErpAuthService(config)
|
||||
const firstSession = await service.login()
|
||||
|
||||
// Second call should return same session
|
||||
const secondSession = await service.login()
|
||||
|
||||
expect(secondSession).toBe(firstSession)
|
||||
expect(chromium.launch).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should throw when forwardFrame contentFrame returns null', async () => {
|
||||
service = new ErpAuthService(config)
|
||||
mockPage.locator = vi.fn().mockReturnValue({
|
||||
contentFrame: vi.fn().mockResolvedValue(null)
|
||||
})
|
||||
|
||||
await expect(service.login()).rejects.toThrow('Failed to access forwardFrame content frame')
|
||||
})
|
||||
})
|
||||
|
||||
describe('close()', () => {
|
||||
beforeEach(() => {
|
||||
service = new ErpAuthService(config)
|
||||
})
|
||||
|
||||
it('should be no-op when not logged in', async () => {
|
||||
// Should not throw when calling close without session
|
||||
await expect(service.close()).resolves.not.toThrow()
|
||||
expect(service.isActive()).toBe(false)
|
||||
})
|
||||
|
||||
it('should clear session when logged in', async () => {
|
||||
// Manually set session state
|
||||
const mockSession = {
|
||||
browser: { close: vi.fn().mockResolvedValue(undefined) },
|
||||
context: { close: vi.fn().mockResolvedValue(undefined) },
|
||||
page: {},
|
||||
mainFrame: {},
|
||||
isLoggedIn: true
|
||||
}
|
||||
;(service as any).session = mockSession
|
||||
|
||||
await service.close()
|
||||
|
||||
expect(service.isActive()).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user