mirror of
https://github.com/strands-agents/harness-sdk.git
synced 2026-10-02 02:44:48 +08:00
Unify bedrock & openai client creation for integ tests (#340)
In order to better share code between browser and node integ tests, use a single implementation for creating clients and inside of the factory methods, do the conditional check for what environment we're running in. As part of this I refactored the setup to take advantage of vitest features of having the global setup inject data into the running tests - including whether we're running in the browser and if we're running in CI/CD. --------- Co-authored-by: Mackenzie Zastrow <zastrowm@users.noreply.github.com>
This commit is contained in:
co-authored by
Mackenzie Zastrow
parent
7fe52cbd5f
commit
2fb71db7c1
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Global setup that runs once before all integration tests and possibly runs in the *parent* process.
|
||||
*
|
||||
* _setup-test on the other hand runs in the *child* process.
|
||||
*/
|
||||
|
||||
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager'
|
||||
import type { TestProject } from 'vitest/node'
|
||||
import type { ProvidedContext } from 'vitest'
|
||||
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
|
||||
|
||||
/**
|
||||
* Load API keys as environment variables from AWS Secrets Manager
|
||||
*/
|
||||
async function loadApiKeysFromSecretsManager(): Promise<void> {
|
||||
const client = new SecretsManagerClient({
|
||||
region: process.env.AWS_REGION || 'us-east-1',
|
||||
})
|
||||
|
||||
try {
|
||||
const secretName = 'model-provider-api-key'
|
||||
const command = new GetSecretValueCommand({
|
||||
SecretId: secretName,
|
||||
})
|
||||
const response = await client.send(command)
|
||||
|
||||
if (response.SecretString) {
|
||||
const secret = JSON.parse(response.SecretString)
|
||||
// Only add API keys for currently supported providers
|
||||
const supportedProviders = ['openai']
|
||||
Object.entries(secret).forEach(([key, value]) => {
|
||||
if (supportedProviders.includes(key.toLowerCase())) {
|
||||
process.env[`${key.toUpperCase()}_API_KEY`] = String(value)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error retrieving secret', e)
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate that required environment variables are set when running in GitHub Actions.
|
||||
* This prevents tests from being unintentionally skipped due to missing credentials.
|
||||
*/
|
||||
if (process.env.GITHUB_ACTIONS !== 'true') {
|
||||
console.warn('Tests running outside GitHub Actions, skipping required provider validation')
|
||||
return
|
||||
}
|
||||
|
||||
const requiredProviders: Set<string> = new Set(['OPENAI_API_KEY'])
|
||||
|
||||
for (const provider of requiredProviders) {
|
||||
if (!process.env[provider]) {
|
||||
throw new Error(`Missing required environment variables for ${provider}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform shared setup for the integration tests.
|
||||
*/
|
||||
export async function setup(project: TestProject): Promise<void> {
|
||||
console.log('Global setup: Loading API keys from Secrets Manager...')
|
||||
await loadApiKeysFromSecretsManager()
|
||||
console.log('Global setup: API keys loaded into environment')
|
||||
|
||||
const isCI = !!globalThis.process.env.CI
|
||||
|
||||
project.provide('isBrowser', project.isBrowserEnabled())
|
||||
project.provide('isCI', isCI)
|
||||
project.provide('provider-openai', await getOpenAITestContext(isCI))
|
||||
project.provide('provider-bedrock', await getBedrockTestContext(isCI))
|
||||
}
|
||||
|
||||
async function getOpenAITestContext(isCI: boolean): Promise<ProvidedContext['provider-openai']> {
|
||||
const apiKey = process.env.OPENAI_API_KEY
|
||||
const shouldSkip = !apiKey
|
||||
|
||||
if (shouldSkip) {
|
||||
console.log('⏭️ OpenAI API key not available - integration tests will be skipped')
|
||||
if (isCI) {
|
||||
throw new Error('CI/CD should be running all tests')
|
||||
}
|
||||
} else {
|
||||
console.log('⏭️ OpenAI API key available - integration tests will run')
|
||||
}
|
||||
|
||||
return {
|
||||
apiKey: apiKey,
|
||||
shouldSkip: shouldSkip,
|
||||
}
|
||||
}
|
||||
|
||||
async function getBedrockTestContext(isCI: boolean): Promise<ProvidedContext['provider-bedrock']> {
|
||||
try {
|
||||
const credentialProvider = fromNodeProviderChain()
|
||||
const credentials = await credentialProvider()
|
||||
console.log('⏭️ Bedrock credentials available - integration tests will run')
|
||||
return {
|
||||
shouldSkip: false,
|
||||
credentials: credentials,
|
||||
}
|
||||
} catch {
|
||||
console.log('⏭️ Bedrock credentials not available - integration tests will be skipped')
|
||||
if (isCI) {
|
||||
throw new Error('CI/CD should be running all tests')
|
||||
}
|
||||
return {
|
||||
shouldSkip: true,
|
||||
credentials: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Test setup that runs once before all integration tests, but in the *child* process.
|
||||
*
|
||||
* _setup-global on the other hand runs in the *parent* process.
|
||||
*/
|
||||
|
||||
import { beforeAll } from 'vitest'
|
||||
import { configureLogging } from '$/sdk/logging/index.js'
|
||||
import { isCI } from './test-helpers.js'
|
||||
|
||||
beforeAll(() => {
|
||||
// When running under CI/CD, preserve all logs including debug
|
||||
if (isCI()) {
|
||||
configureLogging({
|
||||
debug: (...args: unknown[]) => console.debug(...args),
|
||||
info: (...args: unknown[]) => console.info(...args),
|
||||
warn: (...args: unknown[]) => console.warn(...args),
|
||||
error: (...args: unknown[]) => console.error(...args),
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Contains helpers for creating various model providers that work both in node & the browser
|
||||
*/
|
||||
|
||||
import { inject } from 'vitest'
|
||||
import { BedrockModel, type BedrockModelOptions } from '$/sdk/models/bedrock.js'
|
||||
import { OpenAIModel, type OpenAIModelOptions } from '$/sdk/models/openai.js'
|
||||
|
||||
export const bedrock = {
|
||||
name: 'BedrockModel',
|
||||
get skip() {
|
||||
return inject('provider-bedrock').shouldSkip
|
||||
},
|
||||
createModel: (options: BedrockModelOptions = {}): BedrockModel => {
|
||||
const credentials = inject('provider-bedrock').credentials
|
||||
if (!credentials) {
|
||||
throw new Error('No Bedrock credentials provided')
|
||||
}
|
||||
|
||||
return new BedrockModel({
|
||||
...options,
|
||||
clientConfig: {
|
||||
...(options.clientConfig ?? {}),
|
||||
credentials: credentials,
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export const openai = {
|
||||
name: 'OpenAIModel',
|
||||
get skip() {
|
||||
return inject('provider-openai').shouldSkip
|
||||
},
|
||||
createModel: (config: OpenAIModelOptions = {}): OpenAIModel => {
|
||||
const apiKey = inject('provider-openai').apiKey
|
||||
if (!apiKey) {
|
||||
throw new Error('No OpenAI apiKey provided')
|
||||
}
|
||||
|
||||
return new OpenAIModel({
|
||||
...config,
|
||||
apiKey: apiKey,
|
||||
clientConfig: {
|
||||
...(config.clientConfig ?? {}),
|
||||
dangerouslyAllowBrowser: true,
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export const allProviders = [bedrock, openai]
|
||||
@@ -1,4 +1,3 @@
|
||||
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
|
||||
import type { ContentBlock, Message } from '$/sdk/types/messages.js'
|
||||
|
||||
/**
|
||||
@@ -19,68 +18,3 @@ export const getMessageText = (message: Message): string => {
|
||||
.map((block) => block.text)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether AWS integration tests should run based on environment and credentials.
|
||||
*
|
||||
* In CI environments, tests always run (credentials are expected to be configured).
|
||||
* In local environments, tests run only if AWS credentials are available.
|
||||
*
|
||||
* @returns Promise<boolean> - true if tests should run, false if they should be skipped
|
||||
*/
|
||||
export async function shouldSkipBedrockTests(): Promise<boolean> {
|
||||
// In a CI environment, we ALWAYS expect credentials to be configured.
|
||||
// A failure is better than a skip.
|
||||
if (process.env.CI) {
|
||||
console.log('✅ Running in CI environment, integration tests will run.')
|
||||
return false
|
||||
}
|
||||
|
||||
// In a local environment, we check for credentials as a convenience.
|
||||
try {
|
||||
const credentialProvider = fromNodeProviderChain()
|
||||
await credentialProvider()
|
||||
console.log('✅ AWS credentials found locally, integration tests will run.')
|
||||
return false
|
||||
} catch {
|
||||
console.log('⏭️ AWS credentials not available locally, integration tests will be skipped.')
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if OpenAI integration tests should be skipped.
|
||||
* In CI environments, throws an error if API key is missing (tests should not be skipped).
|
||||
* In local development, skips tests if API key is not available.
|
||||
*
|
||||
* @returns true if tests should be skipped, false if they should run
|
||||
* @throws Error if running in CI and API key is missing
|
||||
*/
|
||||
export function shouldSkipOpenAITests(): boolean {
|
||||
try {
|
||||
const isCI = !!process.env.CI
|
||||
const hasKey = !!process.env.OPENAI_API_KEY
|
||||
|
||||
if (isCI && !hasKey) {
|
||||
throw new Error('OpenAI API key must be available in CI environments')
|
||||
}
|
||||
|
||||
if (hasKey) {
|
||||
if (isCI) {
|
||||
console.log('✅ Running in CI environment with OpenAI API key - tests will run')
|
||||
} else {
|
||||
console.log('✅ OpenAI API key found for integration tests')
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
console.log('⏭️ OpenAI API key not available - integration tests will be skipped')
|
||||
return true
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('CI environments')) {
|
||||
throw error
|
||||
}
|
||||
console.log('⏭️ OpenAI API key not available - integration tests will be skipped')
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { inject } from 'vitest'
|
||||
|
||||
/**
|
||||
* Checks whether we're running tests in the browser.
|
||||
*/
|
||||
export const isInBrowser = () => {
|
||||
return globalThis?.process?.env == null
|
||||
return inject('isBrowser')
|
||||
}
|
||||
|
||||
export function isCI() {
|
||||
return inject('isCI')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Agent, DocumentBlock, ImageBlock, Message, TextBlock, tool } from '@strands-agents/sdk'
|
||||
import { BedrockModel } from '@strands-agents/sdk/bedrock'
|
||||
import { notebook } from '@strands-agents/sdk/vended_tools/notebook'
|
||||
import { httpRequest } from '@strands-agents/sdk/vended_tools/http_request'
|
||||
import { OpenAIModel } from '@strands-agents/sdk/openai'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { collectGenerator } from '$/sdk/__fixtures__/model-test-helpers.js'
|
||||
import { shouldSkipBedrockTests, shouldSkipOpenAITests } from './__fixtures__/model-test-helpers.js'
|
||||
import { loadFixture } from './__fixtures__/test-helpers.js'
|
||||
|
||||
// Import fixtures using Vite's ?url suffix
|
||||
import yellowPngUrl from './__resources__/yellow.png?url'
|
||||
import { allProviders } from './__fixtures__/model-providers.js'
|
||||
|
||||
// Calculator tool for testing
|
||||
const calculatorTool = tool({
|
||||
@@ -33,21 +31,7 @@ const calculatorTool = tool({
|
||||
},
|
||||
})
|
||||
|
||||
// Provider configurations
|
||||
const providers = [
|
||||
{
|
||||
name: 'BedrockModel',
|
||||
skip: await shouldSkipBedrockTests(),
|
||||
createModel: () => new BedrockModel(),
|
||||
},
|
||||
{
|
||||
name: 'OpenAIModel',
|
||||
skip: shouldSkipOpenAITests(),
|
||||
createModel: () => new OpenAIModel(),
|
||||
},
|
||||
]
|
||||
|
||||
describe.each(providers)('Agent with $name', ({ name, skip, createModel }) => {
|
||||
describe.each(allProviders)('Agent with $name', ({ name, skip, createModel }) => {
|
||||
describe.skipIf(skip)(`${name} Integration Tests`, () => {
|
||||
describe('Basic Functionality', () => {
|
||||
it('handles invocation, streaming, system prompts, and tool use', async () => {
|
||||
@@ -242,7 +226,7 @@ describe.each(providers)('Agent with $name', ({ name, skip, createModel }) => {
|
||||
|
||||
it('handles tool invocation', async () => {
|
||||
const agent = new Agent({
|
||||
model: await createModel(),
|
||||
model: createModel(),
|
||||
tools: [notebook, httpRequest],
|
||||
printer: false,
|
||||
})
|
||||
|
||||
+44
-47
@@ -1,52 +1,49 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { Agent, BedrockModel } from '$/sdk/index.js'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Agent } from '$/sdk/index.js'
|
||||
import { bash } from '$/sdk/vended-tools/bash/index.js'
|
||||
import { getMessageText, shouldSkipBedrockTests } from './__fixtures__/model-test-helpers.js'
|
||||
import { getMessageText } from './__fixtures__/model-test-helpers.js'
|
||||
import { bedrock } from './__fixtures__/model-providers.js'
|
||||
|
||||
describe.skipIf((await shouldSkipBedrockTests()) || process.platform === 'win32')(
|
||||
'Bash Tool Integration',
|
||||
{ timeout: 60000 },
|
||||
() => {
|
||||
// Shared agent configuration for all tests
|
||||
const createAgent = () =>
|
||||
new Agent({
|
||||
model: new BedrockModel({
|
||||
region: 'us-east-1',
|
||||
}),
|
||||
tools: [bash],
|
||||
})
|
||||
|
||||
describe('basic execution', () => {
|
||||
it('captures stdout streams correctly', async () => {
|
||||
const agent = createAgent()
|
||||
const stdoutResult = await agent.invoke('Use bash to echo "Hello from bash"')
|
||||
expect(getMessageText(stdoutResult.lastMessage)).toContain('Hello from bash')
|
||||
})
|
||||
|
||||
it('captures stderr streams correctly', async () => {
|
||||
const agent = createAgent()
|
||||
const stderrResult = await agent.invoke('Use bash to run: echo "error" >&2')
|
||||
expect(getMessageText(stderrResult.lastMessage)).toContain('error')
|
||||
})
|
||||
|
||||
it('handles complex command patterns', async () => {
|
||||
const agent = createAgent()
|
||||
|
||||
// Test command sequencing
|
||||
const seqResult = await agent.invoke('Use bash to: create a variable TEST=hello, then echo it')
|
||||
expect(getMessageText(seqResult.lastMessage).toLowerCase()).toContain('hello')
|
||||
})
|
||||
describe.skipIf(bedrock.skip || process.platform === 'win32')('Bash Tool Integration', () => {
|
||||
// Shared agent configuration for all tests
|
||||
const createAgent = () =>
|
||||
new Agent({
|
||||
model: bedrock.createModel({
|
||||
region: 'us-east-1',
|
||||
}),
|
||||
tools: [bash],
|
||||
})
|
||||
|
||||
describe('error handling', () => {
|
||||
it('handles command errors gracefully', async () => {
|
||||
const agent = createAgent()
|
||||
const result = await agent.invoke('Use bash to run: nonexistent_command_xyz')
|
||||
|
||||
// Should indicate command not found or error
|
||||
const lastMessage = getMessageText(result.lastMessage).toLowerCase()
|
||||
expect(lastMessage).toMatch(/not found|error|command/)
|
||||
})
|
||||
describe('basic execution', () => {
|
||||
it('captures stdout streams correctly', async () => {
|
||||
const agent = createAgent()
|
||||
const stdoutResult = await agent.invoke('Use bash to echo "Hello from bash"')
|
||||
expect(getMessageText(stdoutResult.lastMessage)).toContain('Hello from bash')
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
it('captures stderr streams correctly', async () => {
|
||||
const agent = createAgent()
|
||||
const stderrResult = await agent.invoke('Use bash to run: echo "error" >&2')
|
||||
expect(getMessageText(stderrResult.lastMessage)).toContain('error')
|
||||
})
|
||||
|
||||
it('handles complex command patterns', async () => {
|
||||
const agent = createAgent()
|
||||
|
||||
// Test command sequencing
|
||||
const seqResult = await agent.invoke('Use bash to: create a variable TEST=hello, then echo it')
|
||||
expect(getMessageText(seqResult.lastMessage).toLowerCase()).toContain('hello')
|
||||
})
|
||||
})
|
||||
|
||||
describe('error handling', () => {
|
||||
it('handles command errors gracefully', async () => {
|
||||
const agent = createAgent()
|
||||
const result = await agent.invoke('Use bash to run: nonexistent_command_xyz')
|
||||
|
||||
// Should indicate command not found or error
|
||||
const lastMessage = getMessageText(result.lastMessage).toLowerCase()
|
||||
expect(lastMessage).toMatch(/not found|error|command/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+17
-18
@@ -1,22 +1,21 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
BedrockModel,
|
||||
Message,
|
||||
Agent,
|
||||
TextBlock,
|
||||
Message,
|
||||
NullConversationManager,
|
||||
SlidingWindowConversationManager,
|
||||
TextBlock,
|
||||
FunctionTool,
|
||||
} from '@strands-agents/sdk'
|
||||
|
||||
import { collectIterator } from '$/sdk/__fixtures__/model-test-helpers.js'
|
||||
import { shouldSkipBedrockTests } from './__fixtures__/model-test-helpers.js'
|
||||
import { bedrock } from './__fixtures__/model-providers.js'
|
||||
|
||||
describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests', () => {
|
||||
describe.skipIf(bedrock.skip)('BedrockModel Integration Tests', () => {
|
||||
describe('Streaming', () => {
|
||||
describe('Configuration', () => {
|
||||
it.concurrent('respects maxTokens configuration', async () => {
|
||||
const provider = new BedrockModel({ maxTokens: 20 })
|
||||
const provider = bedrock.createModel({ maxTokens: 20 })
|
||||
const messages: Message[] = [
|
||||
{
|
||||
type: 'message',
|
||||
@@ -35,7 +34,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
})
|
||||
|
||||
it.concurrent('uses system prompt cache on subsequent requests', async () => {
|
||||
const provider = new BedrockModel({ maxTokens: 100 })
|
||||
const provider = bedrock.createModel({ maxTokens: 100 })
|
||||
const largeContext = `Context information: ${'hello '.repeat(2000)} [test-${Date.now()}-${Math.random()}]`
|
||||
const cachedSystemPrompt = [
|
||||
{ type: 'textBlock' as const, text: 'You are a helpful assistant.' },
|
||||
@@ -63,7 +62,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
})
|
||||
|
||||
it.concurrent('uses message cache points on subsequent requests', async () => {
|
||||
const provider = new BedrockModel({ maxTokens: 100 })
|
||||
const provider = bedrock.createModel({ maxTokens: 100 })
|
||||
const largeContext = `Context information: ${'hello '.repeat(2000)} [test-${Date.now()}-${Math.random()}]`
|
||||
const messagesWithCachePoint = (text: string): Message[] => [
|
||||
{
|
||||
@@ -91,7 +90,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it.concurrent('handles invalid model ID gracefully', async () => {
|
||||
const provider = new BedrockModel({ modelId: 'invalid-model-id-that-does-not-exist' })
|
||||
const provider = bedrock.createModel({ modelId: 'invalid-model-id-that-does-not-exist' })
|
||||
const messages: Message[] = [{ type: 'message', role: 'user', content: [{ type: 'textBlock', text: 'Hello' }] }]
|
||||
await expect(collectIterator(provider.stream(messages))).rejects.toThrow()
|
||||
})
|
||||
@@ -101,7 +100,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
describe('Agent with Conversation Manager', () => {
|
||||
it('manages conversation history with SlidingWindowConversationManager', async () => {
|
||||
const agent = new Agent({
|
||||
model: new BedrockModel({ maxTokens: 100 }),
|
||||
model: bedrock.createModel({ maxTokens: 100 }),
|
||||
conversationManager: new SlidingWindowConversationManager({ windowSize: 4 }),
|
||||
})
|
||||
|
||||
@@ -122,7 +121,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
|
||||
it('throws ContextWindowOverflowError with NullConversationManager', async () => {
|
||||
const agent = new Agent({
|
||||
model: new BedrockModel({ maxTokens: 50 }),
|
||||
model: bedrock.createModel({ maxTokens: 50 }),
|
||||
conversationManager: new NullConversationManager(),
|
||||
})
|
||||
|
||||
@@ -136,7 +135,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
|
||||
describe('Region Configuration', () => {
|
||||
it('uses explicit region when provided', async () => {
|
||||
const provider = new BedrockModel({
|
||||
const provider = bedrock.createModel({
|
||||
region: 'us-east-1',
|
||||
maxTokens: 50,
|
||||
})
|
||||
@@ -152,7 +151,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
vi.stubEnv('AWS_REGION', undefined)
|
||||
vi.stubEnv('AWS_DEFAULT_REGION', undefined)
|
||||
|
||||
const provider = new BedrockModel({
|
||||
const provider = bedrock.createModel({
|
||||
maxTokens: 50,
|
||||
})
|
||||
|
||||
@@ -176,7 +175,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
// Use vitest to stub the environment variable
|
||||
vi.stubEnv('AWS_REGION', 'eu-central-1')
|
||||
|
||||
const provider = new BedrockModel({
|
||||
const provider = bedrock.createModel({
|
||||
maxTokens: 50,
|
||||
})
|
||||
|
||||
@@ -190,7 +189,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
// Use vitest to stub the environment variable
|
||||
vi.stubEnv('AWS_REGION', 'eu-west-1')
|
||||
|
||||
const provider = new BedrockModel({
|
||||
const provider = bedrock.createModel({
|
||||
region: 'ap-southeast-2',
|
||||
maxTokens: 50,
|
||||
})
|
||||
@@ -202,7 +201,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
})
|
||||
|
||||
it('uses region from clientConfig when provided', async () => {
|
||||
const provider = new BedrockModel({
|
||||
const provider = bedrock.createModel({
|
||||
clientConfig: { region: 'ap-northeast-1' },
|
||||
maxTokens: 50,
|
||||
})
|
||||
@@ -239,7 +238,7 @@ describe.skipIf(await shouldSkipBedrockTests())('BedrockModel Integration Tests'
|
||||
|
||||
describe('Thinking Mode with Tools', () => {
|
||||
it('handles thinking mode with tool use', async () => {
|
||||
const bedrockModel = new BedrockModel({
|
||||
const bedrockModel = bedrock.createModel({
|
||||
modelId: 'global.anthropic.claude-sonnet-4-5-20250929-v1:0',
|
||||
additionalRequestFields: {
|
||||
thinking: {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { commands } from 'vitest/browser'
|
||||
import { Agent, DocumentBlock, ImageBlock, Message, TextBlock, tool } from '@strands-agents/sdk'
|
||||
import { BedrockModel } from '@strands-agents/sdk/bedrock'
|
||||
import { OpenAIModel } from '@strands-agents/sdk/openai'
|
||||
import { notebook } from '@strands-agents/sdk/vended_tools/notebook'
|
||||
import { httpRequest } from '@strands-agents/sdk/vended_tools/http_request'
|
||||
import { z } from 'zod'
|
||||
@@ -12,6 +9,7 @@ import { collectGenerator } from '$/sdk/__fixtures__/model-test-helpers.js'
|
||||
// Import fixtures
|
||||
import yellowPngUrl from '../__resources__/yellow.png?url'
|
||||
import { loadFixture } from '../__fixtures__/test-helpers.js'
|
||||
import { allProviders } from '../__fixtures__/model-providers.js'
|
||||
|
||||
// Calculator tool for testing
|
||||
const calculatorTool = tool({
|
||||
@@ -33,36 +31,10 @@ const calculatorTool = tool({
|
||||
},
|
||||
})
|
||||
|
||||
// Provider configurations with browser credential handling
|
||||
const providers = [
|
||||
{
|
||||
name: 'BedrockModel',
|
||||
createModel: async () => {
|
||||
const credentials = await commands.getAwsCredentials()
|
||||
return new BedrockModel({
|
||||
region: 'us-east-1',
|
||||
clientConfig: {
|
||||
credentials,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'OpenAIModel',
|
||||
createModel: async () =>
|
||||
new OpenAIModel({
|
||||
apiKey: await commands.getOpenAIAPIKey(),
|
||||
clientConfig: {
|
||||
dangerouslyAllowBrowser: true,
|
||||
},
|
||||
}),
|
||||
},
|
||||
]
|
||||
|
||||
describe.each(providers)('Agent Browser Tests with $name', async ({ name, createModel }) => {
|
||||
describe(`${name} Browser Integration`, () => {
|
||||
describe.each(allProviders)('Agent Browser Tests with $name', async ({ name, skip, createModel }) => {
|
||||
describe.skipIf(skip)(`${name} Browser Integration`, () => {
|
||||
it('handles basic invocation', async () => {
|
||||
const agent = new Agent({ model: await createModel(), printer: false })
|
||||
const agent = new Agent({ model: createModel(), printer: false })
|
||||
const result = await agent.invoke('Say hello in one word')
|
||||
|
||||
expect(result.stopReason).toBe('endTurn')
|
||||
@@ -72,7 +44,7 @@ describe.each(providers)('Agent Browser Tests with $name', async ({ name, create
|
||||
|
||||
it('handles tool use', async () => {
|
||||
const agent = new Agent({
|
||||
model: await createModel(),
|
||||
model: createModel(),
|
||||
printer: false,
|
||||
systemPrompt: 'Use the calculator tool to solve math problems. Respond with only the numeric result.',
|
||||
tools: [calculatorTool],
|
||||
@@ -103,7 +75,7 @@ describe.each(providers)('Agent Browser Tests with $name', async ({ name, create
|
||||
})
|
||||
|
||||
const agent = new Agent({
|
||||
model: await createModel(),
|
||||
model: createModel(),
|
||||
messages: [
|
||||
new Message({
|
||||
role: 'user',
|
||||
@@ -126,7 +98,7 @@ describe.each(providers)('Agent Browser Tests with $name', async ({ name, create
|
||||
|
||||
it('handles tool invocation', async () => {
|
||||
const agent = new Agent({
|
||||
model: await createModel(),
|
||||
model: createModel(),
|
||||
tools: [notebook, httpRequest],
|
||||
printer: false,
|
||||
})
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { BedrockModel } from '@strands-agents/sdk/bedrock'
|
||||
import { Message, TextBlock } from '@strands-agents/sdk'
|
||||
import { commands } from 'vitest/browser'
|
||||
import { collectIterator } from '$/sdk/__fixtures__/model-test-helpers.js'
|
||||
import { bedrock } from '../__fixtures__/model-providers.js'
|
||||
|
||||
describe('Region Configuration', () => {
|
||||
const sayHighMessage = Message.fromMessageData({
|
||||
@@ -11,12 +10,9 @@ describe('Region Configuration', () => {
|
||||
})
|
||||
|
||||
it('uses explicit region when provided', async () => {
|
||||
const provider = new BedrockModel({
|
||||
const provider = bedrock.createModel({
|
||||
region: 'us-east-1',
|
||||
maxTokens: 50,
|
||||
clientConfig: {
|
||||
credentials: await commands.getAwsCredentials(),
|
||||
},
|
||||
})
|
||||
|
||||
// Validate region configuration by checking config.region() directly
|
||||
@@ -29,11 +25,8 @@ describe('Region Configuration', () => {
|
||||
})
|
||||
|
||||
it('defaults to us-west-2 when no region provided and AWS SDK does not resolve one', async () => {
|
||||
const provider = new BedrockModel({
|
||||
const provider = bedrock.createModel({
|
||||
maxTokens: 50,
|
||||
clientConfig: {
|
||||
credentials: await commands.getAwsCredentials(),
|
||||
},
|
||||
})
|
||||
|
||||
// Validate region defaults to us-west-2
|
||||
@@ -46,8 +39,8 @@ describe('Region Configuration', () => {
|
||||
})
|
||||
|
||||
it('uses region from clientConfig when provided', async () => {
|
||||
const provider = new BedrockModel({
|
||||
clientConfig: { region: 'ap-northeast-1', credentials: await commands.getAwsCredentials() },
|
||||
const provider = bedrock.createModel({
|
||||
clientConfig: { region: 'ap-northeast-1' },
|
||||
maxTokens: 50,
|
||||
})
|
||||
|
||||
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
import type { AwsCredentialIdentity } from '@aws-sdk/types'
|
||||
|
||||
declare module 'vitest/browser' {
|
||||
interface BrowserCommands {
|
||||
getAwsCredentials: () => Promise<AwsCredentialIdentity>
|
||||
getOpenAIAPIKey: () => Promise<string>
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
||||
import { Agent, BedrockModel } from '$/sdk/index.js'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { Agent } from '$/sdk/index.js'
|
||||
import { fileEditor } from '$/sdk/vended-tools/file_editor/index.js'
|
||||
import { collectGenerator } from '$/sdk/__fixtures__/model-test-helpers.js'
|
||||
import { shouldSkipBedrockTests } from './__fixtures__/model-test-helpers.js'
|
||||
import { promises as fs } from 'fs'
|
||||
import * as path from 'path'
|
||||
import { tmpdir } from 'os'
|
||||
import { bedrock } from './__fixtures__/model-providers.js'
|
||||
|
||||
describe.skipIf(await shouldSkipBedrockTests())('FileEditor Tool Integration', () => {
|
||||
describe.skipIf(bedrock.skip)('FileEditor Tool Integration', () => {
|
||||
let testDir: string
|
||||
|
||||
// Shared agent configuration for all tests
|
||||
const createAgent = () =>
|
||||
new Agent({
|
||||
model: new BedrockModel({
|
||||
model: bedrock.createModel({
|
||||
region: 'us-east-1',
|
||||
}),
|
||||
tools: [fileEditor],
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { httpRequest } from '@strands-agents/sdk/vended_tools/http_request'
|
||||
import { Agent, BedrockModel } from '@strands-agents/sdk'
|
||||
import { shouldSkipBedrockTests } from './__fixtures__/model-test-helpers.js'
|
||||
import { Agent } from '@strands-agents/sdk'
|
||||
import { bedrock } from './__fixtures__/model-providers.js'
|
||||
|
||||
describe.skipIf(await shouldSkipBedrockTests())('httpRequest tool (integration)', () => {
|
||||
describe.skipIf(bedrock.skip)('httpRequest tool (integration)', () => {
|
||||
it('agent uses http_request tool to fetch weather from Open-Meteo', async () => {
|
||||
const agent = new Agent({
|
||||
model: new BedrockModel({ maxTokens: 500 }),
|
||||
model: bedrock.createModel({ maxTokens: 500 }),
|
||||
tools: [httpRequest],
|
||||
printer: false,
|
||||
})
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Global setup that runs once before all integration tests
|
||||
* Loads API keys from AWS Secrets Manager into environment variables
|
||||
*/
|
||||
|
||||
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager'
|
||||
|
||||
async function loadApiKeysFromSecretsManager(): Promise<void> {
|
||||
// Load API keys as environment variables from AWS Secrets Manager
|
||||
const client = new SecretsManagerClient({
|
||||
region: process.env.AWS_REGION || 'us-east-1',
|
||||
})
|
||||
console.log('Loading API keys from Secrets Manager')
|
||||
|
||||
try {
|
||||
const secretName = 'model-provider-api-key'
|
||||
const command = new GetSecretValueCommand({
|
||||
SecretId: secretName,
|
||||
})
|
||||
const response = await client.send(command)
|
||||
|
||||
if (response.SecretString) {
|
||||
const secret = JSON.parse(response.SecretString)
|
||||
// Only add API keys for currently supported providers
|
||||
const supportedProviders = ['openai']
|
||||
Object.entries(secret).forEach(([key, value]) => {
|
||||
if (supportedProviders.includes(key.toLowerCase())) {
|
||||
process.env[`${key.toUpperCase()}_API_KEY`] = String(value)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error retrieving secret', e)
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate that required environment variables are set when running in GitHub Actions.
|
||||
* This prevents tests from being unintentionally skipped due to missing credentials.
|
||||
*/
|
||||
if (process.env.GITHUB_ACTIONS !== 'true') {
|
||||
console.warn('Tests running outside GitHub Actions, skipping required provider validation')
|
||||
return
|
||||
}
|
||||
|
||||
const requiredProviders: Set<string> = new Set(['OPENAI_API_KEY'])
|
||||
|
||||
for (const provider of requiredProviders) {
|
||||
if (!process.env[provider]) {
|
||||
throw new Error(`Missing required environment variables for ${provider}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function setup(): Promise<void> {
|
||||
console.log('Global setup: Loading API keys from Secrets Manager...')
|
||||
|
||||
try {
|
||||
await loadApiKeysFromSecretsManager()
|
||||
console.log('Global setup complete: API keys loaded into environment')
|
||||
} catch (error) {
|
||||
console.error('Global setup failed:', error)
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
||||
import { McpClient, Agent } from '@strands-agents/sdk'
|
||||
import { BedrockModel } from '@strands-agents/sdk/bedrock'
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
||||
import { resolve } from 'node:path'
|
||||
import { URL } from 'node:url'
|
||||
import { startHTTPServer, type HttpServerInfo } from './__fixtures__/test-mcp-server.js'
|
||||
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
|
||||
import { bedrock } from './__fixtures__/model-providers.js'
|
||||
|
||||
type TransportConfig = {
|
||||
name: string
|
||||
@@ -64,7 +64,7 @@ describe('MCP Integration Tests', () => {
|
||||
describe.each(transports)('$name transport', ({ createClient }) => {
|
||||
it('agent can use multiple MCP tools in a conversation', async () => {
|
||||
const client = await createClient()
|
||||
const model = new BedrockModel({ maxTokens: 300 })
|
||||
const model = bedrock.createModel({ maxTokens: 300 })
|
||||
|
||||
const agent = new Agent({
|
||||
systemPrompt:
|
||||
@@ -97,7 +97,7 @@ describe('MCP Integration Tests', () => {
|
||||
|
||||
it('agent handles MCP tool errors gracefully', async () => {
|
||||
const client = await createClient()
|
||||
const model = new BedrockModel({ maxTokens: 200 })
|
||||
const model = bedrock.createModel({ maxTokens: 200 })
|
||||
|
||||
const agent = new Agent({
|
||||
systemPrompt: 'You are a helpful assistant. If asked to test errors, use the error_tool.',
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { Agent, BedrockModel } from '$/sdk/index.js'
|
||||
import type { AgentStreamEvent, AgentResult } from '$/sdk/index.js'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { AgentResult, AgentStreamEvent } from '$/sdk/index.js'
|
||||
import { Agent } from '$/sdk/index.js'
|
||||
import { notebook } from '$/sdk/vended-tools/notebook/index.js'
|
||||
import { collectGenerator } from '$/sdk/__fixtures__/model-test-helpers.js'
|
||||
import { shouldSkipBedrockTests } from './__fixtures__/model-test-helpers.js'
|
||||
import { bedrock } from './__fixtures__/model-providers.js'
|
||||
|
||||
describe.skipIf(await shouldSkipBedrockTests())('Notebook Tool Integration', () => {
|
||||
describe.skipIf(bedrock.skip)('Notebook Tool Integration', () => {
|
||||
// Shared agent configuration for all tests
|
||||
const agentParams = {
|
||||
model: new BedrockModel({
|
||||
model: bedrock.createModel({
|
||||
region: 'us-east-1',
|
||||
}),
|
||||
tools: [notebook],
|
||||
|
||||
+11
-12
@@ -1,16 +1,15 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { OpenAIModel } from '@strands-agents/sdk/openai'
|
||||
import { Message } from '@strands-agents/sdk'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { ToolSpec } from '@strands-agents/sdk'
|
||||
import { Message } from '@strands-agents/sdk'
|
||||
|
||||
import { collectIterator } from '$/sdk/__fixtures__/model-test-helpers.js'
|
||||
|
||||
import { shouldSkipOpenAITests } from './__fixtures__/model-test-helpers.js'
|
||||
import { openai } from './__fixtures__/model-providers.js'
|
||||
|
||||
describe.skipIf(shouldSkipOpenAITests())('OpenAIModel Integration Tests', () => {
|
||||
describe.skipIf(openai.skip)('OpenAIModel Integration Tests', () => {
|
||||
describe('Configuration', () => {
|
||||
it.concurrent('respects maxTokens configuration', async () => {
|
||||
const provider = new OpenAIModel({
|
||||
const provider = openai.createModel({
|
||||
modelId: 'gpt-4o-mini',
|
||||
maxTokens: 20, // Very small limit
|
||||
})
|
||||
@@ -34,7 +33,7 @@ describe.skipIf(shouldSkipOpenAITests())('OpenAIModel Integration Tests', () =>
|
||||
})
|
||||
|
||||
it.concurrent('respects temperature configuration', async () => {
|
||||
const provider = new OpenAIModel({
|
||||
const provider = openai.createModel({
|
||||
modelId: 'gpt-4o-mini',
|
||||
temperature: 0, // Deterministic
|
||||
maxTokens: 50,
|
||||
@@ -77,7 +76,7 @@ describe.skipIf(shouldSkipOpenAITests())('OpenAIModel Integration Tests', () =>
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it.concurrent('handles invalid model ID gracefully', async () => {
|
||||
const provider = new OpenAIModel({
|
||||
const provider = openai.createModel({
|
||||
modelId: 'invalid-model-id-that-does-not-exist-xyz',
|
||||
})
|
||||
|
||||
@@ -99,7 +98,7 @@ describe.skipIf(shouldSkipOpenAITests())('OpenAIModel Integration Tests', () =>
|
||||
|
||||
describe('Content Block Lifecycle', () => {
|
||||
it.concurrent('emits complete content block lifecycle events', async () => {
|
||||
const provider = new OpenAIModel({
|
||||
const provider = openai.createModel({
|
||||
modelId: 'gpt-4o-mini',
|
||||
maxTokens: 50,
|
||||
})
|
||||
@@ -139,7 +138,7 @@ describe.skipIf(shouldSkipOpenAITests())('OpenAIModel Integration Tests', () =>
|
||||
|
||||
describe('Stop Reasons', () => {
|
||||
it.concurrent('returns endTurn stop reason for natural completion', async () => {
|
||||
const provider = new OpenAIModel({
|
||||
const provider = openai.createModel({
|
||||
modelId: 'gpt-4o-mini',
|
||||
maxTokens: 100,
|
||||
})
|
||||
@@ -159,7 +158,7 @@ describe.skipIf(shouldSkipOpenAITests())('OpenAIModel Integration Tests', () =>
|
||||
})
|
||||
|
||||
it.concurrent('returns maxTokens stop reason when token limit reached', async () => {
|
||||
const provider = new OpenAIModel({
|
||||
const provider = openai.createModel({
|
||||
modelId: 'gpt-4o-mini',
|
||||
maxTokens: 10, // Very small limit to force cutoff
|
||||
})
|
||||
@@ -179,7 +178,7 @@ describe.skipIf(shouldSkipOpenAITests())('OpenAIModel Integration Tests', () =>
|
||||
})
|
||||
|
||||
it.concurrent('returns toolUse stop reason when requesting tool use', async () => {
|
||||
const provider = new OpenAIModel({
|
||||
const provider = openai.createModel({
|
||||
modelId: 'gpt-4o-mini',
|
||||
maxTokens: 200,
|
||||
})
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"$/sdk/*": ["../../src/*"]
|
||||
},
|
||||
"types": ["vite/client", "vitest/importMeta", "@types/node"]
|
||||
}
|
||||
},
|
||||
"references": [{ "path": "../../src/tsconfig.json" }]
|
||||
}
|
||||
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
import 'vitest'
|
||||
import type { AwsCredentialIdentity } from '@aws-sdk/types'
|
||||
|
||||
declare module 'vitest' {
|
||||
export interface ProvidedContext {
|
||||
isCI: boolean
|
||||
isBrowser: boolean
|
||||
['provider-openai']: {
|
||||
shouldSkip: boolean
|
||||
apiKey: string | undefined
|
||||
}
|
||||
['provider-bedrock']: {
|
||||
shouldSkip: boolean
|
||||
credentials: AwsCredentialIdentity | undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -27,6 +27,6 @@
|
||||
"verbatimModuleSyntax": true,
|
||||
"sourceMap": true,
|
||||
"removeComments": false,
|
||||
"types": ["vitest/importMeta"]
|
||||
"types": ["vite/client", "vitest/importMeta", "@types/node"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-24
@@ -1,9 +1,6 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import { playwright } from '@vitest/browser-playwright'
|
||||
import type { AwsCredentialIdentity } from '@aws-sdk/types'
|
||||
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
|
||||
import type { BrowserCommand } from 'vitest/node'
|
||||
import path from 'path'
|
||||
import * as path from 'node:path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
@@ -15,21 +12,12 @@ if (process.platform === 'win32') {
|
||||
coverageExclude.push('src/vended-tools/bash/**')
|
||||
}
|
||||
|
||||
const getAwsCredentials: BrowserCommand<[], AwsCredentialIdentity> = async ({ testPath, provider }) => {
|
||||
const credentialProvider = fromNodeProviderChain()
|
||||
return await credentialProvider()
|
||||
}
|
||||
|
||||
const getOpenAIAPIKey: BrowserCommand<[], string | undefined> = async ({ testPath, provider }) => {
|
||||
return process.env.OPENAI_API_KEY
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
unstubEnvs: true,
|
||||
reporters: [
|
||||
'default',
|
||||
['junit', { outputFile: 'test/.artifacts/test-report/junit/report.xml' }],
|
||||
['junit', { outputFile: 'test/.artifacts/test-report/junit/report.xml', includeConsoleOutput: true }],
|
||||
['json', { outputFile: 'test/.artifacts/test-report/json/report.json' }],
|
||||
],
|
||||
projects: [
|
||||
@@ -53,6 +41,7 @@ export default defineConfig({
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: playwright(),
|
||||
headless: true,
|
||||
screenshotDirectory: 'test/.artifacts/browser-screenshots/',
|
||||
instances: [
|
||||
{
|
||||
@@ -71,9 +60,10 @@ export default defineConfig({
|
||||
include: ['test/integ/**/*.test.ts'],
|
||||
exclude: ['test/integ/**/*.browser.test.ts'],
|
||||
name: { label: 'integ-node', color: 'magenta' },
|
||||
testTimeout: 30000,
|
||||
testTimeout: 60 * 1000,
|
||||
retry: 1,
|
||||
globalSetup: './test/integ/integ-setup.ts',
|
||||
globalSetup: './test/integ/__fixtures__/_setup-global.ts',
|
||||
setupFiles: './test/integ/__fixtures__/_setup-test.ts',
|
||||
sequence: {
|
||||
concurrent: true,
|
||||
},
|
||||
@@ -87,24 +77,20 @@ export default defineConfig({
|
||||
},
|
||||
include: ['test/integ/**/*.browser.test.ts'],
|
||||
name: { label: 'integ-browser', color: 'yellow' },
|
||||
testTimeout: 30000,
|
||||
testTimeout: 60 * 1000,
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: playwright(),
|
||||
headless: true,
|
||||
screenshotDirectory: 'test/.artifacts/browser-screenshots/',
|
||||
instances: [
|
||||
{
|
||||
browser: 'chromium',
|
||||
},
|
||||
],
|
||||
// These act as passthrough commands that browser tests can use to communicate with the test server running in node.
|
||||
// This allows browsers to get access to credential secrets
|
||||
commands: {
|
||||
getAwsCredentials,
|
||||
getOpenAIAPIKey,
|
||||
},
|
||||
},
|
||||
globalSetup: './test/integ/integ-setup.ts',
|
||||
globalSetup: './test/integ/__fixtures__/_setup-global.ts',
|
||||
setupFiles: './test/integ/__fixtures__/_setup-test.ts',
|
||||
sequence: {
|
||||
concurrent: true,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user