-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
try building test image build test docker image build test docker image build test docker image build test docker image build test docker image build test docker image build test docker image run test image run test image run test image run test image run test image run test image run test image run test image run test image run test image run test image run test run test fix test fix test fix test fix test fix test fix test fix test fix test buildId and customId test readme readme remove container images after run
- Loading branch information
Showing
11 changed files
with
527 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
module.exports = { | ||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { | ||
project: 'tsconfig.json', | ||
tsconfigRootDir: __dirname, | ||
sourceType: 'module', | ||
}, | ||
plugins: ['@typescript-eslint'], | ||
extends: [ | ||
'plugin:@typescript-eslint/recommended', | ||
'plugin:prettier/recommended', | ||
], | ||
root: true, | ||
env: { | ||
node: true, | ||
jest: true, | ||
}, | ||
ignorePatterns: ['.eslintrc.cjs'], | ||
rules: { | ||
'@typescript-eslint/interface-name-prefix': 'off', | ||
'@typescript-eslint/explicit-function-return-type': 'off', | ||
'@typescript-eslint/explicit-module-boundary-types': 'off', | ||
'@typescript-eslint/no-explicit-any': 'off', | ||
|
||
// Allow unused vars that start with _ | ||
"@typescript-eslint/no-unused-vars": [ | ||
"warn", | ||
{ | ||
"argsIgnorePattern": "^_", | ||
"varsIgnorePattern": "^_", | ||
"caughtErrorsIgnorePattern": "^_" | ||
} | ||
], | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Running the tests locally | ||
|
||
Go to the folder of the SDK and build the container image using Docker. | ||
|
||
```sh | ||
docker build -t __SDK_IMAGE_NAME__ . | ||
``` | ||
|
||
Go to tests folder and install the dependencies | ||
|
||
```sh | ||
npm ci | ||
``` | ||
|
||
Set the required environment variables | ||
|
||
```sh | ||
export SAUCE_USERNAME=__YOUR_SAUCE_USERNAME__ | ||
export SAUCE_ACCESS_KEY=__YOUR_SAUCE_ACCESS_KEY__ | ||
export CONTAINER_IMAGE_NAME=__SDK_IMAGE_NAME__ | ||
``` | ||
|
||
Run the tests | ||
|
||
```sh | ||
npm run test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,198 @@ | ||
test('adds 1 + 2 to equal 3', () => { | ||
expect(1+2).toBe(3); | ||
}); | ||
import { BuildStatus, SauceRegion, getApi } from '@saucelabs/visual'; | ||
import { | ||
RE_VISUAL_BUILD_ID, | ||
RE_VISUAL_BUILD_LINK, | ||
SAUCE_VISUAL_BRANCH, | ||
SAUCE_VISUAL_BUILD_NAME, | ||
SAUCE_VISUAL_DEFAULT_BRANCH, | ||
SAUCE_VISUAL_PROJECT, | ||
waitStatusForBuild, | ||
} from './utils/helpers'; | ||
import { execute } from './utils/process'; | ||
import { FileHandle } from 'fs/promises'; | ||
import { randomBytes } from 'crypto'; | ||
|
||
const region = 'us-west-1' as SauceRegion; | ||
|
||
const visualApi = getApi({ | ||
region, | ||
user: process.env.SAUCE_USERNAME!, | ||
key: process.env.SAUCE_ACCESS_KEY!, | ||
}); | ||
|
||
const customId = randomBytes(20).toString('hex'); | ||
|
||
let fileOutput: FileHandle | undefined; | ||
let dockerOutput = ''; | ||
let buildId = ''; | ||
let externalBuildId = ''; | ||
|
||
describe('Env var tests', () => { | ||
it( | ||
'creates an external build', | ||
async () => { | ||
const result = await execute( | ||
`npx @saucelabs/visual build create -n "${SAUCE_VISUAL_BUILD_NAME}"`, | ||
{ | ||
displayOutputOnFailure: true, | ||
pipeOutput: false, | ||
fileOutput, | ||
} | ||
); | ||
expect(result.statusCode).toEqual(0); | ||
const cliOutput = result.stdout; | ||
const buildIds = [...cliOutput.matchAll(RE_VISUAL_BUILD_ID)]; | ||
expect(buildIds.length).toBe(1); | ||
externalBuildId = buildIds[0][1]; | ||
}, | ||
2 * 60 * 1000 | ||
); | ||
|
||
it( | ||
'runs the docker image with SAUCE_VISUAL_BUILD_ID in place', | ||
async () => { | ||
const result = await execute( | ||
`docker run --rm -e SAUCE_USERNAME -e SAUCE_ACCESS_KEY \\ | ||
-e SAUCE_VISUAL_BUILD_ID \\ | ||
${process.env.CONTAINER_IMAGE_NAME}`, | ||
{ | ||
displayOutputOnFailure: true, | ||
pipeOutput: false, | ||
fileOutput, | ||
env: { | ||
SAUCE_VISUAL_BUILD_ID: externalBuildId, | ||
}, | ||
} | ||
); | ||
|
||
expect(result.statusCode).toEqual(0); | ||
dockerOutput = result.stdout; | ||
}, | ||
2 * 60 * 1000 | ||
); | ||
|
||
it( | ||
'screenshots are linked to the external build', | ||
async () => { | ||
const build = await visualApi.buildWithDiffs(externalBuildId); | ||
expect(build).toBeTruthy(); | ||
expect(build?.id).toEqual(externalBuildId); | ||
expect(build?.name).toEqual(SAUCE_VISUAL_BUILD_NAME); | ||
expect(build?.diffs?.nodes.length).toBe(1); | ||
}, | ||
15 * 1000 | ||
); | ||
|
||
it( | ||
'creates an external build with customId', | ||
async () => { | ||
const result = await execute( | ||
`npx @saucelabs/visual build create -n "${SAUCE_VISUAL_BUILD_NAME}" -c ${customId}`, | ||
{ | ||
displayOutputOnFailure: true, | ||
pipeOutput: false, | ||
fileOutput, | ||
} | ||
); | ||
expect(result.statusCode).toEqual(0); | ||
const cliOutput = result.stdout; | ||
const buildIds = [...cliOutput.matchAll(RE_VISUAL_BUILD_ID)]; | ||
expect(buildIds.length).toBe(1); | ||
externalBuildId = buildIds[0][1]; | ||
}, | ||
2 * 60 * 1000 | ||
); | ||
|
||
it( | ||
'runs the docker image with SAUCE_VISUAL_CUSTOM_ID in place', | ||
async () => { | ||
const result = await execute( | ||
`docker run --rm -e SAUCE_USERNAME -e SAUCE_ACCESS_KEY \\ | ||
-e SAUCE_VISUAL_CUSTOM_ID \\ | ||
${process.env.CONTAINER_IMAGE_NAME}`, | ||
{ | ||
displayOutputOnFailure: true, | ||
pipeOutput: false, | ||
fileOutput, | ||
env: { | ||
SAUCE_VISUAL_CUSTOM_ID: customId, | ||
}, | ||
} | ||
); | ||
|
||
expect(result.statusCode).toEqual(0); | ||
dockerOutput = result.stdout; | ||
}, | ||
2 * 60 * 1000 | ||
); | ||
|
||
it( | ||
'screenshots are linked to the external build with customId', | ||
async () => { | ||
const build = await visualApi.buildWithDiffsByCustomId(customId); | ||
expect(build).toBeTruthy(); | ||
expect(build?.id).toEqual(externalBuildId); | ||
expect(build?.name).toEqual(SAUCE_VISUAL_BUILD_NAME); | ||
expect(build?.diffs?.nodes.length).toBe(1); | ||
}, | ||
15 * 1000 | ||
); | ||
|
||
it( | ||
'runs the docker image with env vars in place', | ||
async () => { | ||
const result = await execute( | ||
`docker run --rm -e SAUCE_USERNAME -e SAUCE_ACCESS_KEY \\ | ||
-e SAUCE_VISUAL_PROJECT \\ | ||
-e SAUCE_VISUAL_BRANCH \\ | ||
-e SAUCE_VISUAL_DEFAULT_BRANCH \\ | ||
-e SAUCE_VISUAL_BUILD_NAME \\ | ||
${process.env.CONTAINER_IMAGE_NAME}`, | ||
{ | ||
displayOutputOnFailure: true, | ||
pipeOutput: false, | ||
fileOutput, | ||
env: { | ||
SAUCE_VISUAL_PROJECT: SAUCE_VISUAL_PROJECT, | ||
SAUCE_VISUAL_BRANCH: SAUCE_VISUAL_BRANCH, | ||
SAUCE_VISUAL_DEFAULT_BRANCH: SAUCE_VISUAL_DEFAULT_BRANCH, | ||
SAUCE_VISUAL_BUILD_NAME: SAUCE_VISUAL_BUILD_NAME, | ||
}, | ||
} | ||
); | ||
|
||
expect(result.statusCode).toEqual(0); | ||
dockerOutput = result.stdout; | ||
}, | ||
2 * 60 * 1000 | ||
); | ||
|
||
it('returns a valid build link', async () => { | ||
expect(dockerOutput.length).toBeGreaterThan(0); | ||
|
||
const links = [...dockerOutput.matchAll(RE_VISUAL_BUILD_LINK)]; | ||
expect(links.length).toBe(1); | ||
buildId = links[0][4]; | ||
}); | ||
|
||
it( | ||
'env vars are processed correctly', | ||
async () => { | ||
expect(buildId).toMatch(RE_VISUAL_BUILD_ID); | ||
|
||
await waitStatusForBuild(visualApi, buildId, [BuildStatus.Unapproved], { | ||
refreshRate: 1000, | ||
retries: 10, | ||
}); | ||
|
||
const build = await visualApi.build(buildId); | ||
expect(build).toBeTruthy(); | ||
expect(build?.id).toEqual(buildId); | ||
expect(build?.name).toEqual(SAUCE_VISUAL_BUILD_NAME); | ||
expect(build?.project).toBe(SAUCE_VISUAL_PROJECT); | ||
expect(build?.branch).toBe(SAUCE_VISUAL_BRANCH); | ||
expect(build?.defaultBranch).toBe(SAUCE_VISUAL_DEFAULT_BRANCH); | ||
}, | ||
15 * 1000 | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** @type {import('ts-jest').JestConfigWithTsJest} */ | ||
module.exports = { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
testMatch: ['**/tests/*.spec.ts'], | ||
testPathIgnorePatterns: ['/project/', '/node_modules/'], | ||
runner: './utils/runner.ts', | ||
setupFilesAfterEnv: ['jest-extended/all'], | ||
bail: 1, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,4 +22,4 @@ | |
"dependencies": { | ||
"@saucelabs/visual": "^0.3.352" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Build, BuildStatus, VisualApi } from '@saucelabs/visual'; | ||
|
||
export const RE_VISUAL_BUILD_LINK = | ||
/https:\/\/app\.(([a-z0-9-]+)\.)?saucelabs\.(com|net)\/visual\/builds\/([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})/g; | ||
export const RE_VISUAL_BUILD_ID = | ||
/([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})/g; | ||
|
||
export const SAUCE_VISUAL_PROJECT = 'E2E test project'; | ||
export const SAUCE_VISUAL_BRANCH = 'E2E test branch'; | ||
export const SAUCE_VISUAL_DEFAULT_BRANCH = 'E2E test default branch'; | ||
export const SAUCE_VISUAL_BUILD_NAME = 'E2E test build name'; | ||
|
||
export const waitStatusForBuild = async function ( | ||
api: VisualApi, | ||
buildId: string, | ||
status: BuildStatus[], | ||
options?: { | ||
refreshRate?: number; | ||
retries?: number; | ||
buildIdType?: 'customId' | 'buildId'; | ||
} | ||
): Promise<Partial<Build> | undefined> { | ||
const { | ||
refreshRate = 500, | ||
buildIdType = 'buildId', | ||
retries = 10, | ||
} = options ?? {}; | ||
let currentTry = 0; | ||
do { | ||
currentTry++; | ||
const build = | ||
buildIdType === 'buildId' | ||
? await api.build(buildId) | ||
: await api.buildByCustomId(buildId); | ||
if (build?.status && status.includes(build?.status)) { | ||
return build; | ||
} | ||
await wait(refreshRate); | ||
} while (currentTry < retries); | ||
throw new Error( | ||
`Expected status ${status} never received for build ${buildId}` | ||
); | ||
}; | ||
|
||
async function wait(delayMs: number): Promise<void> { | ||
return new Promise((resolve) => { | ||
setTimeout(resolve, delayMs); | ||
}); | ||
} |
Oops, something went wrong.