diff --git a/.github/workflows/_check_code.yaml b/.github/workflows/_check_code.yaml index 97bcaa3..08f16ed 100644 --- a/.github/workflows/_check_code.yaml +++ b/.github/workflows/_check_code.yaml @@ -16,7 +16,7 @@ jobs: - name: Run actionlint uses: rhysd/actionlint@v1.7.11 - # TODO: Fix spell check after we merge current PRs + # TODO: Fix spell check after we merge current PRs # spell_check: # name: Spell check # runs-on: ubuntu-latest @@ -26,23 +26,22 @@ jobs: # - name: Check spelling with typos # uses: crate-ci/typos@v1 - # TODO: Fix lint after we merge current PRs - # lint_check: - # name: Lint check - # runs-on: ubuntu-latest - # steps: - # - name: Checkout repository - # uses: actions/checkout@v6 - # - name: Use Node.js - # uses: actions/setup-node@v6 - # with: - # node-version: 24 - # cache: 'npm' - # cache-dependency-path: 'package-lock.json' - # - name: Install dependencies - # run: npm ci - # - name: Lint - # run: npm run lint + lint_check: + name: Lint check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Use Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: 'npm' + cache-dependency-path: 'package-lock.json' + - name: Install dependencies + run: npm ci + - name: Lint + run: npm run lint type_check: name: Type check diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..b485109 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,13 @@ +echo "pre-commit: typechecking, linting, testing, checking code formatting, checking for unused exports & validating schemas" + + +npm test +npm run format:check +# We typecheck & lint last when we know our code works +npx tsc --noEmit +npx lint-staged + +# Activate once we resolv unused delete old builds +# npm run check-unused + +echo "pre-commit: passed" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ca5f88..9bef826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. + ## 0.5.7 - **not yet released** ### 🚀 Features @@ -14,8 +15,8 @@ All notable changes to this project will be documented in this file. - Bump version to test beta release ([1322d31](https://github.com/apify/apify-test-tools/commit/1322d31873b6d43e16a68e97bdc358752f813f79)) by [@metalwarrior665](https://github.com/metalwarrior665) - + # Changelog ## 0.5.5 @@ -61,6 +62,7 @@ feat: feat: add maxRetriesPerRequest test ## 0.2.3 ### Lib + - feat: add `runId` option to test tests - fix: PPE pass won't override overall pass @@ -89,4 +91,4 @@ feat: feat: add maxRetriesPerRequest test ### Cli - fix: parsing commits -- feat: add `--workspace` cli option \ No newline at end of file +- feat: add `--workspace` cli option diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9fd2f2d..8cf97b1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,7 @@ # Contributing The package consists of two parts: + - cli located in `bin/` - test library located in `lib` @@ -22,6 +23,7 @@ The package consists of two parts: ### Development setup 1. Clone and build `apify-test-tools` repo: + ```sh git clone git@github.com:apify-projects/apify-test-tools.git cd apify-test-tools @@ -30,6 +32,7 @@ npm run build ``` For testing purposes, we use `testing-repo-for-github-actions` repo so that we don't mess with the production repos: + ```sh git clone git@github.com:apify-store/testing-repo-for-github-actions.git ``` @@ -37,6 +40,7 @@ git clone git@github.com:apify-store/testing-repo-for-github-actions.git #### Working on the CLI To work on the library, you just need to define `GITHUB_WORKSPACE` to tell the cli where you repo is located: + ```sh export GITHUB_WORKSPACE=../path/to/testing-repo-for-github-actions # path to the repo npx tsx bin/main.ts --help @@ -46,6 +50,7 @@ npx tsx bin/main.ts get-commits --target-branch master --source-branch feat/test #### Working on the library You need to istall the local version of `apify-test-tools` in your cloned `testing-repo-for-github-actions`: + ```sh npm i -D ../path/to/apify-test-tools ``` diff --git a/README.md b/README.md index 0c1cc6c..d756ba6 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Getting Started 1. Install the package `npm i -D apify-test-tools` - - because it uses [annotate](https://vitest.dev/guide/test-context.html#annotate), `vitest` version to be at least `3.2.0` + - because it uses [annotate](https://vitest.dev/guide/test-context.html#annotate), `vitest` version to be at least `3.2.0` - make sure that `target` and `module` in your `tsconfig.json`'s `compilerOptions` are set to `ES2022` 2. create test directories: `mkdir -p test/platform/core` - core (hourly) tests should go to `test/platform/core` @@ -13,6 +13,7 @@ 3. setup github worklows TODO File structure: + ``` google-maps ├── actors @@ -20,7 +21,7 @@ google-maps └── test ├── unit └── platform - ├── core <- Core tests need to be inside core directory + ├── core <- Core tests need to be inside core directory │ └── core.test.ts ├── some.test.ts <- Other tests can be defined anywhere inside platform directory └── some-other.test.ts @@ -73,7 +74,7 @@ name: PR Test on: pull_request: - branches: [ master ] + branches: [master] jobs: buildDevelAndTest: @@ -88,7 +89,7 @@ name: Release latest on: push: - branches: [ master ] + branches: [master] jobs: buildLatest: @@ -102,9 +103,10 @@ jobs: ### Test structure -To run the tests concurrently, we had to start the run outside of `it` and then call `await` inside. This is now no longer needed and everything can be inside `it` aka `testActor`. +To run the tests concurrently, we had to start the run outside of `it` and then call `await` inside. This is now no longer needed and everything can be inside `it` aka `testActor`. Before: + ```ts ({ it, xit, run, expect, expectAsync, input, describe }: TestSpecInputs) => { describe('test', () => { @@ -112,7 +114,7 @@ Before: const runPromise = run({ actorId, input }) it('actor test 1', async () => { const runResult = await runPromise; - + // your checks }); } @@ -121,25 +123,26 @@ Before: const runPromise = run({ actorId, input }) it('actor test 2', async () => { const runResult = await runPromise; - + // your checks }); - } + } }); }) ``` After: + ```ts import { describe, testActor } from 'apify-test-tools'; describe('test', () => { testActor(actorId, 'actor test 1', async ({ expect, run }) => { const runResult = await run({ input }) - + // your checks )}; - + testActor(actorId, 'actor test 2', async ({ expect, run }) => { const runResult = await run({ input }) @@ -155,6 +158,7 @@ describe('test', () => { ### Validating basic run attributes Before: + ```ts await expectAsync(runResult).toHaveStatus('SUCCEEDED'); @@ -164,41 +168,36 @@ await expectAsync(runResult).withLog((log) => { }); await expectAsync(runResult).withStatistics((stats) => { - expect(stats.requestsRetries) - .withContext(runResult.format('Request retries')) - .toBeLessThan(3); - expect(stats.crawlerRuntimeMillis) - .withContext(runResult.format('Run time')) - .toBeWithinRange(600, 600_000) -}) - + expect(stats.requestsRetries).withContext(runResult.format('Request retries')).toBeLessThan(3); + expect(stats.crawlerRuntimeMillis).withContext(runResult.format('Run time')).toBeWithinRange(600, 600_000); +}); + await expectAsync(runResult).withDataset(({ dataset }) => { - expect(dataset.items?.length) - .withContext(runResult.format('Dataset cleanItemCount')) - .toBe(100); -}) + expect(dataset.items?.length).withContext(runResult.format('Dataset cleanItemCount')).toBe(100); +}); ``` After: + ```ts await expect(runResult).toFinishWith({ - datasetItemCount: 100, -}) + datasetItemCount: 100, +}); ``` You can also specify a range: ```ts await expect(runResult).toFinishWith({ - datasetItemCount: { min: 80, max: 120 }, -}) + datasetItemCount: { min: 80, max: 120 }, +}); ``` Here is full example of what you can validate with `toFinishWith` ```ts await expect(runResult).toFinishWith({ - // These are default + // These are default status: 'SUCCEEDED', duration: { min: 600, // 0.6 sec @@ -206,20 +205,17 @@ await expect(runResult).toFinishWith({ }, failedRequests: 0, requestsRetries: { max: 3 }, - forbiddenLogs: [ - 'ReferenceError', - 'TypeError', - ], - - // only datasetItemCount is required - datasetItemCount: { min: 80, max: 120 }, - + forbiddenLogs: ['ReferenceError', 'TypeError'], + + // only datasetItemCount is required + datasetItemCount: { min: 80, max: 120 }, + // optional chargedEventCounts: { - 'actor-start': 1, - 'place-scraped': 9, - }, -}) + 'actor-start': 1, + 'place-scraped': 9, + }, +}); ``` --- @@ -227,15 +223,15 @@ await expect(runResult).toFinishWith({ ### Custom validations Before: + ```ts -expect(place.title) - .withContext(runResult.format(`London Eye's title`)) - .toEqual('lastminute.com London Eye') +expect(place.title).withContext(runResult.format(`London Eye's title`)).toEqual('lastminute.com London Eye'); ``` After: + ```ts -expect(place.title, `London Eye's title`).toEqual('lastminute.com London Eye') +expect(place.title, `London Eye's title`).toEqual('lastminute.com London Eye'); ``` --- diff --git a/bin/build.ts b/bin/build.ts index 9747aa1..5b7b1aa 100644 --- a/bin/build.ts +++ b/bin/build.ts @@ -1,25 +1,36 @@ +import type { Build } from 'apify-client'; +import { ApifyClient } from 'apify-client'; + import { ACTOR_SOURCE_TYPES } from '@apify/consts'; -import { ApifyClient, Build } from 'apify-client'; + import type { ActorConfig, BuildData } from './types.js'; import { getEnvVar } from './utils.js'; type BuildPrActorOptions = { - buildTag?: string - versionNumber: string - gitRepoUrl: string - actorName: string -} + buildTag?: string; + versionNumber: string; + gitRepoUrl: string; + actorName: string; +}; class ApifyBuilder { - // eslint-disable-next-line no-empty-function - private constructor(private readonly apifyClient: ApifyClient, private readonly actorName: string) { } + private constructor( + private readonly apifyClient: ApifyClient, + private readonly actorName: string, + ) {} // Usually 'latest' but not necessarily (can be e.g. 'version-0') - getDefaultVersionAndTag = async (): Promise<{ defaultBuildNumber: string, defaultVersionNumber: string, defaultBuildTag: string }> => { + getDefaultVersionAndTag = async (): Promise<{ + defaultBuildNumber: string; + defaultVersionNumber: string; + defaultBuildTag: string; + }> => { const actorClient = this.apifyClient.actor(this.actorName); const actorInfo = await actorClient.get(); if (!actorInfo) { - throw new Error(`[${this.actorName}] not found. It is not published or we are missing token to access it privately or its name is misspelled`); + throw new Error( + `[${this.actorName}] not found. It is not published or we are missing token to access it privately or its name is misspelled`, + ); } const defaultBuildTag = actorInfo.defaultRunOptions.build; @@ -27,8 +38,10 @@ class ApifyBuilder { // We could technically allow this but in most cases this is accidentally set wrongly and there is a workaround if (defaultBuildTag.match(/\d+\.\d+\.\d+/)) { - throw new Error(`[${this.actorName}] Default build is a build number, not a tag. While this could work, ` - + `we want to have a default as tag so this is often an accidental misconfiguration from the dev`); + throw new Error( + `[${this.actorName}] Default build is a build number, not a tag. While this could work, ` + + `we want to have a default as tag so this is often an accidental misconfiguration from the dev`, + ); } // I reported that buildNumber should probably not be optional const defaultBuildNumber = actorInfo.taggedBuilds![defaultBuildTag].buildNumber!; @@ -38,17 +51,15 @@ class ApifyBuilder { return { defaultBuildNumber, defaultVersionNumber, defaultBuildTag }; }; - startActorBuild = async ({ - buildTag, - versionNumber, - gitRepoUrl, - }: BuildPrActorOptions): Promise => { + startActorBuild = async ({ buildTag, versionNumber, gitRepoUrl }: BuildPrActorOptions): Promise => { const actorClient = this.apifyClient.actor(this.actorName); const actorInfo = await actorClient.get(); if (!actorInfo) { - throw new Error(`No actor named '${this.actorName}' was found on the platform. If this` - + ' is unexpected, make sure the actor you are targeting is spelled the' - + ' same as the folder in the repository.'); + throw new Error( + `No actor named '${this.actorName}' was found on the platform. If this` + + ' is unexpected, make sure the actor you are targeting is spelled the' + + ' same as the folder in the repository.', + ); } // NOTE: I couldn't find this type, so I had to extract it :( @@ -83,8 +94,9 @@ class ApifyBuilder { const build = await this.apifyClient.build(buildId).waitForFinish(); const versionNumber = build.buildNumber; if (build.status === 'FAILED' || build.status === 'TIMED-OUT') { - const message = `[BUILD][${actorName}]: Build ${buildId} (${versionNumber}) failed. ` - + `Not continuing with other builds and tests.`; + const message = + `[BUILD][${actorName}]: Build ${buildId} (${versionNumber}) failed. ` + + `Not continuing with other builds and tests.`; console.error(`[${this.actorName}]: ${versionNumber}`); throw new Error(message); } @@ -93,8 +105,8 @@ class ApifyBuilder { }; /** - * Create ApifyBuilder with actor owner's token - */ + * Create ApifyBuilder with actor owner's token + */ static fromActorName = (actorName: string): ApifyBuilder => { const username = actorName.split('/')[0]; // GitHib secrets only allow word characters (alphanum + underscore) @@ -102,8 +114,10 @@ class ApifyBuilder { const usernameEnvVar = `APIFY_TOKEN_${usernameInGitHubSecretsFormat}`; const token = process.env[usernameEnvVar]; if (!token) { - throw new Error(`Cannot find Apify API token for username: ${username}. ` - + `Have you set secret env var to this GitHub repo with key: ${usernameEnvVar}?`); + throw new Error( + `Cannot find Apify API token for username: ${username}. ` + + `Have you set secret env var to this GitHub repo with key: ${usernameEnvVar}?`, + ); } const apifyClient = new ApifyClient({ token }); const builder = new ApifyBuilder(apifyClient, actorName); @@ -118,7 +132,21 @@ class ApifyBuilder { // Even though we don't version our current Actors, if we ever such Actors to GitHub CI, we would accidentally delete old supported versions // This hardcoded solution is not ideal, but it should prevent most imaginable cases // All currently popular versioned Actors use `version-${number}` format - const PROTECTED_TAGS_PREFIX = ['latest', 'v-', 'version', 'v0', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 'v8', 'v9']; + const PROTECTED_TAGS_PREFIX = [ + 'latest', + 'v-', + 'version', + 'v0', + 'v1', + 'v2', + 'v3', + 'v4', + 'v5', + 'v6', + 'v7', + 'v8', + 'v9', + ]; // We don't want to be too short because we might to debug something // but also not too long because it increases the risk of users using outdated versions @@ -132,43 +160,58 @@ class ApifyBuilder { const allTags = Object.keys(actorInfo.taggedBuilds ?? {}); const protectedTags = allTags.filter((tag) => PROTECTED_TAGS_PREFIX.some((prefix) => tag.startsWith(prefix))); - const protectedBuildNumbers = protectedTags.map((tag) => ({ buildNumber: actorInfo.taggedBuilds![tag]!.buildNumber, tag })); + const protectedBuildNumbers = protectedTags.map((tag) => ({ + buildNumber: actorInfo.taggedBuilds![tag]!.buildNumber, + tag, + })); - const { items } = (await this.apifyClient.actor(this.actorName).builds().list()); + const { items } = await this.apifyClient.actor(this.actorName).builds().list(); // Deleting default build throws an error, so we skip it - const { defaultBuildNumber, defaultBuildTag } = await ApifyBuilder.fromActorName(this.actorName).getDefaultVersionAndTag(); + const { defaultBuildNumber, defaultBuildTag } = await ApifyBuilder.fromActorName( + this.actorName, + ).getDefaultVersionAndTag(); const daysAgoUnixProd = Date.now() - DEFAULT_DAYS_BACK_PROD_VERSIONS * 24 * 60 * 60 * 1000; const daysAgoUnixDevel = Date.now() - DEFAULT_DAYS_BACK_DEVEL * 24 * 60 * 60 * 1000; // Fixing API client missing buildNumber field - type CorrectBuildColletionItem = typeof items[0] & { buildNumber: string }; + type CorrectBuildColletionItem = (typeof items)[0] & { buildNumber: string }; const buildsToDelete = (items as CorrectBuildColletionItem[]).filter((build) => { if (build.buildNumber === defaultBuildNumber) { - console.error(`[DELETE OLD BUILDS][${this.actorName}]: Skipping default build ${defaultBuildNumber} (${defaultBuildTag}). ` - + `We never delete default builds`); + console.error( + `[DELETE OLD BUILDS][${this.actorName}]: Skipping default build ${defaultBuildNumber} (${defaultBuildTag}). ` + + `We never delete default builds`, + ); return false; } - const protectedTagFound = protectedBuildNumbers.find((protectedBuildNumber) => protectedBuildNumber.buildNumber === build.buildNumber); + const protectedTagFound = protectedBuildNumbers.find( + (protectedBuildNumber) => protectedBuildNumber.buildNumber === build.buildNumber, + ); if (protectedTagFound) { - console.error(`[DELETE OLD BUILDS][${this.actorName}]: Skipping protected build ${protectedTagFound.buildNumber} (${protectedTagFound.tag}).`); + console.error( + `[DELETE OLD BUILDS][${this.actorName}]: Skipping protected build ${protectedTagFound.buildNumber} (${protectedTagFound.tag}).`, + ); return false; } if (taggedDevelBuildNumber && build.buildNumber === taggedDevelBuildNumber) { const shouldDeleteDevelBuild = build.startedAt.getTime() < daysAgoUnixDevel; if (shouldDeleteDevelBuild) { - console.error(`[DELETE OLD BUILDS][${this.actorName}]: Removing olf devel build ${taggedDevelBuildNumber}.`); + console.error( + `[DELETE OLD BUILDS][${this.actorName}]: Removing olf devel build ${taggedDevelBuildNumber}.`, + ); } return shouldDeleteDevelBuild; } return build.startedAt.getTime() < daysAgoUnixProd; }); - console.error(`[DELETE OLD BUILDS][${this.actorName}]: Deleting ${buildsToDelete.length} old builds that are non-default and ` - + `older than 30 days from total ${items.length}`); + console.error( + `[DELETE OLD BUILDS][${this.actorName}]: Deleting ${buildsToDelete.length} old builds that are non-default and ` + + `older than 30 days from total ${items.length}`, + ); for (const build of buildsToDelete) { await this.apifyClient.build(build.id).delete(); } @@ -176,20 +219,14 @@ class ApifyBuilder { } type RunBuildsOptions = { - actorConfigs: ActorConfig[] - isLatest?: boolean - repoUrl: string - branch: string - dryRun: boolean -} + actorConfigs: ActorConfig[]; + isLatest?: boolean; + repoUrl: string; + branch: string; + dryRun: boolean; +}; -export const runBuilds = async ({ - repoUrl, - branch, - actorConfigs, - isLatest = false, - dryRun, -}: RunBuildsOptions) => { +export const runBuilds = async ({ repoUrl, branch, actorConfigs, isLatest = false, dryRun }: RunBuildsOptions) => { const buildConfigs: BuildPrActorOptions[] = []; const circleActors = isLatest ? await findCircleApifyManaged(actorConfigs) : []; @@ -199,7 +236,8 @@ export const runBuilds = async ({ let buildTag: string | undefined; if (isLatest) { - const { defaultVersionNumber, defaultBuildTag } = await ApifyBuilder.fromActorName(actorName).getDefaultVersionAndTag(); + const { defaultVersionNumber, defaultBuildTag } = + await ApifyBuilder.fromActorName(actorName).getDefaultVersionAndTag(); versionNumber = defaultVersionNumber; buildTag = defaultBuildTag; } else { @@ -217,25 +255,29 @@ export const runBuilds = async ({ if (dryRun) { return buildConfigs; } - console.error("========================================="); - console.error("STARTED BUILDS:"); - const startedBuilds = await Promise.all(buildConfigs.map(async (buildConfig) => { - const builder = ApifyBuilder.fromActorName(buildConfig.actorName); - const buildData = await builder.startActorBuild(buildConfig); - return buildData; - })); - console.error("========================================="); - console.error("FINISHED BUILDS:"); - await Promise.all(startedBuilds.map(async (buildData) => { - const builder = ApifyBuilder.fromActorName(buildData.actorName); - await builder.waitForBuildToFinish(buildData.buildId, buildData.actorName); - })); - console.error("========================================="); - console.error("SUMMARY:"); + console.error('========================================='); + console.error('STARTED BUILDS:'); + const startedBuilds = await Promise.all( + buildConfigs.map(async (buildConfig) => { + const builder = ApifyBuilder.fromActorName(buildConfig.actorName); + const buildData = await builder.startActorBuild(buildConfig); + return buildData; + }), + ); + console.error('========================================='); + console.error('FINISHED BUILDS:'); + await Promise.all( + startedBuilds.map(async (buildData) => { + const builder = ApifyBuilder.fromActorName(buildData.actorName); + await builder.waitForBuildToFinish(buildData.buildId, buildData.actorName); + }), + ); + console.error('========================================='); + console.error('SUMMARY:'); for (const buildData of startedBuilds.sort((a, b) => a.actorName.localeCompare(b.actorName))) { console.error(`[${buildData.actorName}]: ${buildData.buildNumber} `); } - console.error("========================================="); + console.error('========================================='); return startedBuilds; }; @@ -259,27 +301,36 @@ const findCircleApifyManaged = async (actorConfigs: ActorConfig[]): Promise((circleActor) => { - // They prefix all with apify-managed---, I communicated with Jacques to keep doing that - let actorConfigFound = actorConfigs.find((actorConfig) => circleActor.name.replace('apify-managed---', '') === actorConfig.actorName.split('/')[1]); - - // Hack for bad naming of circ_le/apify-managed-google-search, we don't want to rename now to break customers - if (!actorConfigFound && circleActor.name === 'apify-managed-google-search') { - actorConfigFound = actorConfigs.find((actorConfig) => actorConfig.actorName.split('/')[1] === 'google-search-scraper'); - } + const actorsToBuild = circleActors + .map((circleActor) => { + // They prefix all with apify-managed---, I communicated with Jacques to keep doing that + let actorConfigFound = actorConfigs.find( + (actorConfig) => + circleActor.name.replace('apify-managed---', '') === actorConfig.actorName.split('/')[1], + ); + + // Hack for bad naming of circ_le/apify-managed-google-search, we don't want to rename now to break customers + if (!actorConfigFound && circleActor.name === 'apify-managed-google-search') { + actorConfigFound = actorConfigs.find( + (actorConfig) => actorConfig.actorName.split('/')[1] === 'google-search-scraper', + ); + } - if (actorConfigFound) { - return { - // We point the circle Actor to the repo folder - actorName: `${circleActor.username}/${circleActor.name}`, - folder: actorConfigFound.folder, - isStandalone: actorConfigFound.isStandalone, - }; - } - return undefined; - }).filter((config) => config !== undefined); + if (actorConfigFound) { + return { + // We point the circle Actor to the repo folder + actorName: `${circleActor.username}/${circleActor.name}`, + folder: actorConfigFound.folder, + isStandalone: actorConfigFound.isStandalone, + }; + } + return undefined; + }) + .filter((config) => config !== undefined); - console.error(`Found ${actorsToBuild.length} circ_le actors that match Actors we built out of total ${circleActors.length} circ_le actors`); + console.error( + `Found ${actorsToBuild.length} circ_le actors that match Actors we built out of total ${circleActors.length} circ_le actors`, + ); console.error(`All circ_le actors: ${circleActors.map((actor) => actor.name).join(', ')}`); console.error(`circ_le Actors to build: ${actorsToBuild.map((actor) => actor.actorName).join(', ')}`); diff --git a/bin/consts.ts b/bin/consts.ts deleted file mode 100644 index a02c50a..0000000 --- a/bin/consts.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Technically, upgrades in shared or packages might not need impact every actor -// but we cannot really know that -export const DEFAULT_BUILD_ALL_FOLDERS = ['code', 'shared', 'packages']; - -export const PERSISTED_GH_JOBS_KVS_ID = 'XIBdO8EJePdD0KiAI'; -export const MINIACTORS_LIST_STORE_ID = 'ftXklt2f2mN30Oc3y'; diff --git a/bin/diff-changes.ts b/bin/diff-changes.ts index 13707c6..300dae1 100644 --- a/bin/diff-changes.ts +++ b/bin/diff-changes.ts @@ -8,35 +8,48 @@ interface ShouldBuildAndTestOptions { commits: Commit[]; } -export const maybeParseActorFolder = (lowercaseFilePath: string): { isActorFolder: true, actorName: string } | { isActorFolder: false } => { +export const maybeParseActorFolder = ( + lowercaseFilePath: string, +): { isActorFolder: true; actorName: string } | { isActorFolder: false } => { const match = lowercaseFilePath.match(/^(?:standalone-)?actors\/([^/]+)\/.+/); if (match) { // Some usernames weirdly use underscores, e.g. google_maps_email_extractor_standby-contact-details-scraper so we only need replace the last one return { isActorFolder: true, actorName: match[1].replace(/_(?=[^_]*$)/, '/') }; } return { isActorFolder: false }; -} +}; /** * Also works for folders */ const isIgnoredTopLevelFile = (lowercaseFilePath: string) => { // On top level, we should only have dev-only readme and .actor/ is just for apify push CLI (real Actor configs are in /actors) - const IGNORED_TOP_LEVEL_FILES = ['.vscode/', '.gitignore', 'readme.md', '.husky/', '.eslintrc', 'eslint.config.mjs', '.prettierrc', '.editorconfig', '.actor/']; + const IGNORED_TOP_LEVEL_FILES = [ + '.vscode/', + '.gitignore', + 'readme.md', + '.husky/', + '.eslintrc', + 'eslint.config.mjs', + '.prettierrc', + '.editorconfig', + '.actor/', + ]; // Strip out deprecated /code and /shared folders, treat them as top-level code const sanitizedLowercaseFilePath = lowercaseFilePath.replace(/^code\//, '').replace(/^shared\//, ''); return IGNORED_TOP_LEVEL_FILES.some((ignoredFile) => sanitizedLowercaseFilePath.startsWith(ignoredFile)); }; -type FileChange = - { impact: 'ignored' } | +type FileChange = + | { impact: 'ignored' } // Only things that influence how the Actor looks - e.g. README and CHANGELOG files, schema titles, descriptions, reordering, etc. We only need to rebuild on release - { impact: 'cosmetic', includes: 'all-actors' | ActorConfig } | + | { impact: 'cosmetic'; includes: 'all-actors' | ActorConfig } // Influences how the Actor works - we need to run tests - { - impact: 'functional', includes: 'all-actors' | ActorConfig - }; + | { + impact: 'functional'; + includes: 'all-actors' | ActorConfig; + }; const classifyFileChange = (lowercaseFilePath: string, actorConfigs: ActorConfig[], commits: Commit[]): FileChange => { if (isIgnoredTopLevelFile(lowercaseFilePath)) { @@ -49,13 +62,18 @@ const classifyFileChange = (lowercaseFilePath: string, actorConfigs: ActorConfig const actorFolderInfo = maybeParseActorFolder(lowercaseFilePath); if (actorFolderInfo.isActorFolder) { - const actorConfigChanged = actorConfigs.find(({ actorName }) => actorName.toLowerCase() === actorFolderInfo.actorName); + const actorConfigChanged = actorConfigs.find( + ({ actorName }) => actorName.toLowerCase() === actorFolderInfo.actorName, + ); // This is some super weird case that happened once in the past but I don't remember the context anymore if (actorConfigChanged === undefined) { - console.error('SHOULD NEVER HAPPEN: changes was found in an actor folder which no longer exists in the current commit, skipping this file', { - actorName: actorFolderInfo.actorName, - lowercaseFilePath, - }); + console.error( + 'SHOULD NEVER HAPPEN: changes was found in an actor folder which no longer exists in the current commit, skipping this file', + { + actorName: actorFolderInfo.actorName, + lowercaseFilePath, + }, + ); return { impact: 'ignored' }; } if (lowercaseFilePath.endsWith('readme.md')) { @@ -70,11 +88,14 @@ const classifyFileChange = (lowercaseFilePath: string, actorConfigs: ActorConfig // For any other files, we assume they can interact with the code return { impact: 'functional', includes: 'all-actors' }; -} +}; -export const getChangedActors = ( - { filepathsChanged, actorConfigs, isLatest = false, commits }: ShouldBuildAndTestOptions, -): ActorConfig[] => { +export const getChangedActors = ({ + filepathsChanged, + actorConfigs, + isLatest = false, + commits, +}: ShouldBuildAndTestOptions): ActorConfig[] => { // folder -> ActorConfig const actorsChangedMap = new Map(); @@ -105,23 +126,31 @@ export const getChangedActors = ( const actorsChanged = Array.from(actorsChangedMap.values()); // All below here is just for logging - const ignoredFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'ignored'); + const ignoredFilesChanged = lowercaseFiles.filter( + (file) => classifyFileChange(file, actorConfigs, commits).impact === 'ignored', + ); console.error(`[DIFF]: Ignored files (don't trigger test or build): ${ignoredFilesChanged.join(', ')}`); - const cosmeticFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'cosmetic'); + const cosmeticFilesChanged = lowercaseFiles.filter( + (file) => classifyFileChange(file, actorConfigs, commits).impact === 'cosmetic', + ); console.error(`[DIFF]: Cosmetic files (should only trigger release build): ${cosmeticFilesChanged.join(', ')}`); - const functionalFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'functional'); + const functionalFilesChanged = lowercaseFiles.filter( + (file) => classifyFileChange(file, actorConfigs, commits).impact === 'functional', + ); console.error(`[DIFF]: Functional files (trigger test & release build): ${functionalFilesChanged.join(', ')}`); if (actorsChanged.length > 0) { const miniactors = actorsChanged.filter((config) => !config.isStandalone).map((config) => config.actorName); - const standaloneActors = actorsChanged.filter((config) => config.isStandalone).map((config) => config.actorName); + const standaloneActors = actorsChanged + .filter((config) => config.isStandalone) + .map((config) => config.actorName); console.error(`[DIFF]: MiniActors to be built and tested: ${miniactors.join(', ')}`); console.error(`[DIFF]: Standalone Actors to be built and tested: ${standaloneActors.join(', ')}`); } else { console.error(`[DIFF]: No relevant files changed, skipping builds and tests`); } - + return actorsChanged; }; diff --git a/bin/diff-json-schema.ts b/bin/diff-json-schema.ts index 964c6ed..3f24162 100644 --- a/bin/diff-json-schema.ts +++ b/bin/diff-json-schema.ts @@ -2,7 +2,12 @@ import type { Commit } from './types.js'; import { spawnCommandInGhWorkspace } from './utils.js'; const COSMETIC_JSON_FIELD_NAMES = new Set([ - 'title', 'description', 'example', 'enumTitles', 'sectionCaption', 'sectionDescription', + 'title', + 'description', + 'example', + 'enumTitles', + 'sectionCaption', + 'sectionDescription', ]); const isPlainObject = (val: unknown): val is Record => @@ -32,11 +37,13 @@ export const isCosmeticOnlyJsonSchemaChange = (commits: Commit[], changedFilepat try { const oldContent = spawnCommandInGhWorkspace(`git show ${oldRef}:${changedFilepath}`); const newContent = spawnCommandInGhWorkspace(`git show ${newRef}:${changedFilepath}`); - + oldJson = JSON.parse(oldContent); newJson = JSON.parse(newContent); } catch { - console.error(`Failed to get or parse JSON content for ${changedFilepath} at refs ${oldRef} and ${newRef}, maybe it is new file or deleted? Treating it as a non-cosmetic change.`); + console.error( + `Failed to get or parse JSON content for ${changedFilepath} at refs ${oldRef} and ${newRef}, maybe it is new file or deleted? Treating it as a non-cosmetic change.`, + ); return false; } return isCosmeticObjectChange(oldJson, newJson); diff --git a/bin/git.ts b/bin/git.ts index 6b7dd18..ceb69d6 100644 --- a/bin/git.ts +++ b/bin/git.ts @@ -33,22 +33,20 @@ export const getCommits = ({ sourceBranch, targetBranch, baseCommit: baseCommitS const hasBaseCommit = baseCommitIndex !== -1; if (hasBaseCommit) { const commitsUpToBaseCommit = commits.slice(baseCommitIndex + 1); - console.error(`Found base commit ${baseCommitSha} at index ${baseCommitIndex}, returning ${commitsUpToBaseCommit.length} commits after it`); + console.error( + `Found base commit ${baseCommitSha} at index ${baseCommitIndex}, returning ${commitsUpToBaseCommit.length} commits after it`, + ); console.error(`Commits being returned: ${commitsUpToBaseCommit.map((c) => c.sha).join(', ')}`); return commitsUpToBaseCommit; } - console.error(`Base commit ${baseCommitSha} not found in the commit range, returning all ${commits.length} commits`); + console.error( + `Base commit ${baseCommitSha} not found in the commit range, returning all ${commits.length} commits`, + ); console.error(`Commits being returned: ${commits.map((c) => c.sha).join(', ')}`); return commits; }; -export const getCommitInfo = (commitSha: string): Commit => { - const commitString = spawnCommandInGhWorkspace(`git log -1 --pretty=format:'${GIT_LOG_FORMAT}' ${commitSha}`); - const commit = parseCommit(commitString); - return commit; -}; - export const parseCommit = (commitString: string): Commit => { const splits = commitString.split(GIT_FORMAT_SEPARATOR); if (splits.length !== 4) { diff --git a/bin/github.ts b/bin/github.ts index 2511447..6211b82 100644 --- a/bin/github.ts +++ b/bin/github.ts @@ -1,5 +1,6 @@ -import fs from 'fs/promises'; -import { Commit, GitHubEventPush } from './types.js'; +import fs from 'node:fs/promises'; + +import type { Commit, GitHubEventPush } from './types.js'; import { spawnCommandInGhWorkspace } from './utils.js'; export const getPushData = async (path: string) => { @@ -10,8 +11,8 @@ export const getPushData = async (path: string) => { repository: { ssh_url: repoUrl, name }, head_commit: { author }, } = event; - const commits: Commit[] = ghCommits.map(({ author, message, id, timestamp }) => ({ - author: author.username, + const commits: Commit[] = ghCommits.map(({ author: commitAuthor, message, id, timestamp }) => ({ + author: commitAuthor.username, sha: id, message, date: timestamp, @@ -29,7 +30,7 @@ export const getPushData = async (path: string) => { }; }; -export const loadGitHubEvent = async (path: string): Promise => { +const loadGitHubEvent = async (path: string): Promise => { const pushEvent = JSON.parse((await fs.readFile(path)).toString()) as GitHubEventPush; return pushEvent; }; @@ -39,7 +40,7 @@ const getChangedFiles = ({ after, before }: GitHubEventPush): string[] => { return changedFiles.split('\n'); }; -export const getChangelogChanges = (changedFiles: string[], event: GitHubEventPush): string | null => { +const getChangelogChanges = (changedFiles: string[], event: GitHubEventPush): string | null => { const changelogPath = 'CHANGELOG.md'; if (!changedFiles.includes(changelogPath)) { return null; diff --git a/bin/main.ts b/bin/main.ts index f1082a2..4d445ad 100644 --- a/bin/main.ts +++ b/bin/main.ts @@ -3,6 +3,7 @@ import process from 'node:process'; import yargs, { type Argv } from 'yargs'; +// eslint-disable-next-line import/extensions --- With .js, it cannot find types import { hideBin } from 'yargs/helpers'; import { runBuilds } from './build.js'; @@ -11,7 +12,7 @@ import { getChangedFiles, getCommits } from './git.js'; import { getPushData } from './github.js'; import { notifyToSlack } from './slack.js'; import { reportTestResults } from './test-report.js'; -import { getRepoActors, setCwd,spawnCommandInGhWorkspace } from './utils.js'; +import { getRepoActors, setCwd, spawnCommandInGhWorkspace } from './utils.js'; /** * Middlewares to be run before every command execution @@ -65,13 +66,18 @@ await yargs() async () => { const actorConfigs = await getRepoActors(); console.log(JSON.stringify(actorConfigs)); - } + }, ) .command('get-affected-actors', '', buildOptions, async (args) => { const commits = getCommits(args); const changedFiles = getChangedFiles(commits); const actorConfigs = await getRepoActors(); - const actorsChanged = getChangedActors({ filepathsChanged: changedFiles, actorConfigs, isLatest: false, commits }); + const actorsChanged = getChangedActors({ + filepathsChanged: changedFiles, + actorConfigs, + isLatest: false, + commits, + }); console.log(JSON.stringify(actorsChanged)); }) .command( @@ -85,7 +91,7 @@ await yargs() .option('workflow-name', { type: 'string' }), async (args) => { await reportTestResults(args); - } + }, ) .command( 'build', @@ -104,7 +110,7 @@ await yargs() // git@github.com:apify-store/google-maps#:actors/lukaskrivka_google-maps-with-contact-details const repoUrl = spawnCommandInGhWorkspace(`git remote get-url origin`).replace( /^https:\/\/github\.com\//, - 'git@github.com:' + 'git@github.com:', ); const builds = await runBuilds({ @@ -114,7 +120,7 @@ await yargs() dryRun: args.dryRun, }); console.log(JSON.stringify(builds)); - } + }, ) .command( 'release', @@ -127,7 +133,7 @@ await yargs() .option('release-slack-channel', { type: 'string' }), async (args) => { const { branch, changedFiles, repoUrl, commits, changelog, repository, author } = await getPushData( - args.pushEventPath + args.pushEventPath, ); const isLatest = true; const actorConfigs = await getRepoActors(); @@ -158,7 +164,7 @@ await yargs() reportSlackChannel, releaseSlackChannel, }); - } + }, ) .strictCommands() .demandCommand(1, 'Command is required') diff --git a/bin/slack.ts b/bin/slack.ts index 30dcfbf..0e69d94 100644 --- a/bin/slack.ts +++ b/bin/slack.ts @@ -1,5 +1,6 @@ import { WebClient } from '@slack/web-api'; -import { Commit } from './types.js'; + +import type { Commit } from './types.js'; import { getEnvVar } from './utils.js'; type NotifyToSlackOptions = { @@ -46,7 +47,10 @@ export const notifyToSlack = async ({ } const commitsMessage = `${commits - .map(({ author, message }, index) => `${index + 1}. Commit message: ${message}\n\tAuthor: ${author}.`) + .map( + ({ author: commitAuthor, message }, index) => + `${index + 1}. Commit message: ${message}\n\tAuthor: ${commitAuthor}.`, + ) .join('\n')}`; const changedFilesMessage = `**Files changed**: ${changedFiles.join(', ')}`; const longMessage = `${shortMessage}\n**Commit list**:\n${commitsMessage}\n\n${changedFilesMessage}`; diff --git a/bin/test-report.ts b/bin/test-report.ts index 4ba9e88..257f356 100644 --- a/bin/test-report.ts +++ b/bin/test-report.ts @@ -1,13 +1,14 @@ -import fs from 'fs/promises'; +import fs from 'node:fs/promises'; + import { sendSlackMessage } from './slack.js'; import { getEnvVar } from './utils.js'; interface ReportTestResultsOptions { - reportFile: string - dryRun: boolean - reportSlackChannel?: string - jobUrl?: string - workflowName?: string + reportFile: string; + dryRun: boolean; + reportSlackChannel?: string; + jobUrl?: string; + workflowName?: string; } export const reportTestResults = async ({ @@ -35,7 +36,7 @@ export const reportTestResults = async ({ } } - const failedAssertions: { message: string; runLink: string, actorName: string }[] = []; + const failedAssertions: { message: string; runLink: string; actorName: string }[] = []; console.error(); console.error(`PASSED: ${passed.length}, FAILED: ${failed.length}`); @@ -57,7 +58,13 @@ export const reportTestResults = async ({ for (const [i, aResult] of failed.entries()) { const { failureMessages, fullName, meta } = aResult; if (failureMessages) { - failedAssertions.push(...failureMessages.map(message => ({ message: message.split('\n')?.[0], runLink: meta.runLink, actorName: meta.actorName }))); + failedAssertions.push( + ...failureMessages.map((message) => ({ + message: message.split('\n')?.[0], + runLink: meta.runLink, + actorName: meta.actorName, + })), + ); } console.error(`${i + 1}) ${fullName} ... ${meta.runLink}`); console.error(); @@ -65,9 +72,11 @@ export const reportTestResults = async ({ console.error(); console.error(`PASSED: ${passed.length}, FAILED: ${failed.length}`); console.error(); - + if (!reportSlackChannel) { - console.error(`Skipping slack notification. If you want to enable it, add --report-slack-channel flag and make sure SLACK_TOKEN_TESTS_BOT env variable is set.`); + console.error( + `Skipping slack notification. If you want to enable it, add --report-slack-channel flag and make sure SLACK_TOKEN_TESTS_BOT env variable is set.`, + ); return; } @@ -81,7 +90,9 @@ export const reportTestResults = async ({ let slackMessage = `\`${workflowName ?? '-'}\``; slackMessage += `: has ${failedAssertions.length} failed assertions. Failing test suites: ${failed.length}/${total}.${jobLink}`; slackMessage += `\n\n${failedAssertions[0].message} --- <${failedAssertions[0].runLink}|${failedAssertions[0].actorName}>`; - const blocks = failedAssertions.slice(1).map(({ message, runLink, actorName }) => `• ${message} --- <${runLink}|${actorName}>`); + const blocks = failedAssertions + .slice(1) + .map(({ message, runLink, actorName }) => `• ${message} --- <${runLink}|${actorName}>`); console.error('SLACK:', slackMessage); console.error('\tblocks:', blocks.join('\n\t\t')); @@ -96,52 +107,52 @@ export const reportTestResults = async ({ } }; -type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo' | 'disabled' -type Milliseconds = number +type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo' | 'disabled'; +type Milliseconds = number; interface Callsite { - line: number - column: number + line: number; + column: number; } interface JsonAssertionResult { - ancestorTitles: Array - fullName: string - status: Status - title: string + ancestorTitles: string[]; + fullName: string; + status: Status; + title: string; meta: { - runId: string - runLink: string - actorName: string - } - duration?: Milliseconds | null - failureMessages: Array | null - location?: Callsite | null + runId: string; + runLink: string; + actorName: string; + }; + duration?: Milliseconds | null; + failureMessages: string[] | null; + location?: Callsite | null; } interface JsonTestResult { - message: string - name: string - status: 'failed' | 'passed' - startTime: number - endTime: number - assertionResults: Array + message: string; + name: string; + status: 'failed' | 'passed'; + startTime: number; + endTime: number; + assertionResults: JsonAssertionResult[]; // summary: string // coverage: unknown } interface JsonTestResults { - numFailedTests: number - numFailedTestSuites: number - numPassedTests: number - numPassedTestSuites: number - numPendingTests: number - numPendingTestSuites: number - numTodoTests: number - numTotalTests: number - numTotalTestSuites: number - startTime: number - success: boolean - testResults: Array + numFailedTests: number; + numFailedTestSuites: number; + numPassedTests: number; + numPassedTestSuites: number; + numPendingTests: number; + numPendingTestSuites: number; + numTodoTests: number; + numTotalTests: number; + numTotalTestSuites: number; + startTime: number; + success: boolean; + testResults: JsonTestResult[]; // snapshot: SnapshotSummary // coverageMap?: CoverageMap | null | undefined // numRuntimeErrorTestSuites: number diff --git a/bin/types.ts b/bin/types.ts index 6c0dbfb..9f60717 100644 --- a/bin/types.ts +++ b/bin/types.ts @@ -1,91 +1,88 @@ export interface Config { - targetBranch: string - sourceBranch: string - baseCommit?: string - workspace?: string + targetBranch: string; + sourceBranch: string; + baseCommit?: string; + workspace?: string; } export type Commit = { - sha: string - author: string - date: string - message: string -} + sha: string; + author: string; + date: string; + message: string; +}; // NOTE: The GitHub types are incomplete, feel free to add fields/complete (not sure how stable GitHub API is) export interface GitHubHeadCommit { - added: string[] - removed: string[] - modified: string[], + added: string[]; + removed: string[]; + modified: string[]; author: { - name: string - }, - message: string, - id: string, + name: string; + }; + message: string; + id: string; } export interface Repository { - full_name: string - name: string - ssh_url: string + full_name: string; + name: string; + ssh_url: string; owner: { - login: string - } + login: string; + }; } export type GitHubEvent = GitHubEventPullRequest | GitHubEventPush; -// All optional type so we check that we have all required fields for each event type -export type RawGitHubEvent = Partial & Omit>; - export interface GitHubEventPullRequest { // Type is our own variable we inject to have discriminated union // The rest of the properties are raw from GitHub API - type: 'pull_request', + type: 'pull_request'; pull_request: { - number: number + number: number; base: { - ref: string, - sha: string - } + ref: string; + sha: string; + }; head: { - ref: string - sha: string - } - } - repository: Repository, + ref: string; + sha: string; + }; + }; + repository: Repository; } export interface GitHubEventPush { - type: 'push', - head_commit: GitHubHeadCommit, - repository: Repository, - ref: string - commits: GithubCommit[] - before: string - after: string + type: 'push'; + head_commit: GitHubHeadCommit; + repository: Repository; + ref: string; + commits: GithubCommit[]; + before: string; + after: string; } export interface GithubCommit { - id: string - tree_id: string - distinct: boolean - message: string - timestamp: string - url: string + id: string; + tree_id: string; + distinct: boolean; + message: string; + timestamp: string; + url: string; author: { - name: string - email: string - username: string - } + name: string; + email: string; + username: string; + }; committer: { - name: string - email: string - username: string - } - added: string[] - removed: string[] - modified: string[] + name: string; + email: string; + username: string; + }; + added: string[]; + removed: string[]; + modified: string[]; } export interface BuildData { @@ -99,5 +96,5 @@ export interface BuildData { export interface ActorConfig { actorName: string; folder: string; - isStandalone: boolean + isStandalone: boolean; } diff --git a/bin/utils.ts b/bin/utils.ts index 4fc34fd..264dadf 100644 --- a/bin/utils.ts +++ b/bin/utils.ts @@ -1,10 +1,7 @@ import { spawnSync } from 'node:child_process'; import fs from 'node:fs/promises'; -import type { - ActorConfig, - GitHubEvent, -} from './types.js'; +import type { ActorConfig, GitHubEvent } from './types.js'; export const spawnCommandInGhWorkspace = (command: string, args: string[] = []) => { console.error(command, args.join(' ')); @@ -49,14 +46,14 @@ export const getRepoActors = async (): Promise => { let actorDirs: string[]; try { actorDirs = (await fs.readdir(`./actors`)).map((dir) => `actors/${dir}`); - } catch (err) { + } catch { console.warn(`No /actors directory found in repo`); actorDirs = []; } let standaloneActorDirs: string[]; try { standaloneActorDirs = (await fs.readdir(`./standalone-actors`)).map((dir) => `standalone-actors/${dir}`); - } catch (err) { + } catch { console.warn(`No /standalone-actors directory found in repo`); standaloneActorDirs = []; } @@ -73,8 +70,18 @@ export const getRepoActors = async (): Promise => { isStandalone: folderType === 'standalone-actors', }); } - console.error(`Actors in repo: ${actorConfigs.filter(({ isStandalone }) => !isStandalone).map(({ actorName }) => actorName).join(', ')}`); - console.error(`Standalone actors in repo: ${actorConfigs.filter(({ isStandalone }) => !!isStandalone).map(({ actorName }) => actorName).join(', ')}`); + console.error( + `Actors in repo: ${actorConfigs + .filter(({ isStandalone }) => !isStandalone) + .map(({ actorName }) => actorName) + .join(', ')}`, + ); + console.error( + `Standalone actors in repo: ${actorConfigs + .filter(({ isStandalone }) => !!isStandalone) + .map(({ actorName }) => actorName) + .join(', ')}`, + ); return actorConfigs; }; @@ -85,10 +92,8 @@ export const setCwd = ({ workspace }: { workspace: string | undefined }) => { } const ghWorkspace = getEnvVar('GITHUB_WORKSPACE', process.cwd()); process.chdir(ghWorkspace); -} +}; export const getHeadCommitSha = (githubEvent: GitHubEvent) => { - return githubEvent.type === 'pull_request' - ? githubEvent.pull_request.head.sha - : githubEvent.head_commit.id; + return githubEvent.type === 'pull_request' ? githubEvent.pull_request.head.sha : githubEvent.head_commit.id; }; diff --git a/eslint.config.mjs b/eslint.config.mjs index ba3c4ec..131a61d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,7 +6,7 @@ import tsEslint from 'typescript-eslint'; // eslint-disable-next-line import/no-default-export export default [ - { ignores: ['**/dist', 'eslint.config.mjs'] }, + { ignores: ['**/dist', 'eslint.config.mjs', '.github'] }, ...apify, prettier, { @@ -25,6 +25,8 @@ export default [ }, rules: { 'no-console': 0, + // This was used heavily, I don't have string opinion so turning it off for now, feel free to refactor later + 'no-use-before-define': 'off', }, }, -]; \ No newline at end of file +]; diff --git a/example-github-events/pull-request.json b/example-github-events/pull-request.json deleted file mode 100644 index ec290fa..0000000 --- a/example-github-events/pull-request.json +++ /dev/null @@ -1,523 +0,0 @@ -{ - "action": "synchronize", - "after": "c8b2b890bf0b2d2af80029f5ab02b6523c45c714", - "before": "cb5be6abb5038590a1927ea0a598a9d5b5e54e2a", - "number": 136, - "organization": { - "avatar_url": "https://avatars.githubusercontent.com/u/69005150?v=4", - "description": "This organization houses projects that are not part of Apify itself, such as enterprise or marketplace developed actors and scrapers.", - "events_url": "https://api.github.com/orgs/apify-projects/events", - "hooks_url": "https://api.github.com/orgs/apify-projects/hooks", - "id": 69005150, - "issues_url": "https://api.github.com/orgs/apify-projects/issues", - "login": "apify-projects", - "members_url": "https://api.github.com/orgs/apify-projects/members{/member}", - "node_id": "MDEyOk9yZ2FuaXphdGlvbjY5MDA1MTUw", - "public_members_url": "https://api.github.com/orgs/apify-projects/public_members{/member}", - "repos_url": "https://api.github.com/orgs/apify-projects/repos", - "url": "https://api.github.com/orgs/apify-projects" - }, - "pull_request": { - "_links": { - "comments": { - "href": "https://api.github.com/repos/apify-projects/store-youtube/issues/136/comments" - }, - "commits": { - "href": "https://api.github.com/repos/apify-projects/store-youtube/pulls/136/commits" - }, - "html": { - "href": "https://github.com/apify-projects/store-youtube/pull/136" - }, - "issue": { - "href": "https://api.github.com/repos/apify-projects/store-youtube/issues/136" - }, - "review_comment": { - "href": "https://api.github.com/repos/apify-projects/store-youtube/pulls/comments{/number}" - }, - "review_comments": { - "href": "https://api.github.com/repos/apify-projects/store-youtube/pulls/136/comments" - }, - "self": { - "href": "https://api.github.com/repos/apify-projects/store-youtube/pulls/136" - }, - "statuses": { - "href": "https://api.github.com/repos/apify-projects/store-youtube/statuses/c8b2b890bf0b2d2af80029f5ab02b6523c45c714" - } - }, - "active_lock_reason": null, - "additions": 6, - "assignee": null, - "assignees": [], - "author_association": "CONTRIBUTOR", - "auto_merge": null, - "base": { - "label": "apify-projects:master", - "ref": "master", - "repo": { - "allow_auto_merge": false, - "allow_forking": true, - "allow_merge_commit": false, - "allow_rebase_merge": true, - "allow_squash_merge": true, - "allow_update_branch": true, - "archive_url": "https://api.github.com/repos/apify-projects/store-youtube/{archive_format}{/ref}", - "archived": false, - "assignees_url": "https://api.github.com/repos/apify-projects/store-youtube/assignees{/user}", - "blobs_url": "https://api.github.com/repos/apify-projects/store-youtube/git/blobs{/sha}", - "branches_url": "https://api.github.com/repos/apify-projects/store-youtube/branches{/branch}", - "clone_url": "https://github.com/apify-projects/store-youtube.git", - "collaborators_url": "https://api.github.com/repos/apify-projects/store-youtube/collaborators{/collaborator}", - "comments_url": "https://api.github.com/repos/apify-projects/store-youtube/comments{/number}", - "commits_url": "https://api.github.com/repos/apify-projects/store-youtube/commits{/sha}", - "compare_url": "https://api.github.com/repos/apify-projects/store-youtube/compare/{base}...{head}", - "contents_url": "https://api.github.com/repos/apify-projects/store-youtube/contents/{+path}", - "contributors_url": "https://api.github.com/repos/apify-projects/store-youtube/contributors", - "created_at": "2022-09-01T18:56:38Z", - "default_branch": "master", - "delete_branch_on_merge": true, - "deployments_url": "https://api.github.com/repos/apify-projects/store-youtube/deployments", - "description": null, - "disabled": false, - "downloads_url": "https://api.github.com/repos/apify-projects/store-youtube/downloads", - "events_url": "https://api.github.com/repos/apify-projects/store-youtube/events", - "fork": false, - "forks": 1, - "forks_count": 1, - "forks_url": "https://api.github.com/repos/apify-projects/store-youtube/forks", - "full_name": "apify-projects/store-youtube", - "git_commits_url": "https://api.github.com/repos/apify-projects/store-youtube/git/commits{/sha}", - "git_refs_url": "https://api.github.com/repos/apify-projects/store-youtube/git/refs{/sha}", - "git_tags_url": "https://api.github.com/repos/apify-projects/store-youtube/git/tags{/sha}", - "git_url": "git://github.com/apify-projects/store-youtube.git", - "has_discussions": false, - "has_downloads": true, - "has_issues": true, - "has_pages": false, - "has_projects": true, - "has_wiki": false, - "homepage": null, - "hooks_url": "https://api.github.com/repos/apify-projects/store-youtube/hooks", - "html_url": "https://github.com/apify-projects/store-youtube", - "id": 531649207, - "is_template": false, - "issue_comment_url": "https://api.github.com/repos/apify-projects/store-youtube/issues/comments{/number}", - "issue_events_url": "https://api.github.com/repos/apify-projects/store-youtube/issues/events{/number}", - "issues_url": "https://api.github.com/repos/apify-projects/store-youtube/issues{/number}", - "keys_url": "https://api.github.com/repos/apify-projects/store-youtube/keys{/key_id}", - "labels_url": "https://api.github.com/repos/apify-projects/store-youtube/labels{/name}", - "language": "TypeScript", - "languages_url": "https://api.github.com/repos/apify-projects/store-youtube/languages", - "license": { - "key": "apache-2.0", - "name": "Apache License 2.0", - "node_id": "MDc6TGljZW5zZTI=", - "spdx_id": "Apache-2.0", - "url": "https://api.github.com/licenses/apache-2.0" - }, - "merge_commit_message": "PR_TITLE", - "merge_commit_title": "MERGE_MESSAGE", - "merges_url": "https://api.github.com/repos/apify-projects/store-youtube/merges", - "milestones_url": "https://api.github.com/repos/apify-projects/store-youtube/milestones{/number}", - "mirror_url": null, - "name": "store-youtube", - "node_id": "R_kgDOH7BStw", - "notifications_url": "https://api.github.com/repos/apify-projects/store-youtube/notifications{?since,all,participating}", - "open_issues": 10, - "open_issues_count": 10, - "owner": { - "avatar_url": "https://avatars.githubusercontent.com/u/69005150?v=4", - "events_url": "https://api.github.com/users/apify-projects/events{/privacy}", - "followers_url": "https://api.github.com/users/apify-projects/followers", - "following_url": "https://api.github.com/users/apify-projects/following{/other_user}", - "gists_url": "https://api.github.com/users/apify-projects/gists{/gist_id}", - "gravatar_id": "", - "html_url": "https://github.com/apify-projects", - "id": 69005150, - "login": "apify-projects", - "node_id": "MDEyOk9yZ2FuaXphdGlvbjY5MDA1MTUw", - "organizations_url": "https://api.github.com/users/apify-projects/orgs", - "received_events_url": "https://api.github.com/users/apify-projects/received_events", - "repos_url": "https://api.github.com/users/apify-projects/repos", - "site_admin": false, - "starred_url": "https://api.github.com/users/apify-projects/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/apify-projects/subscriptions", - "type": "Organization", - "url": "https://api.github.com/users/apify-projects" - }, - "private": true, - "pulls_url": "https://api.github.com/repos/apify-projects/store-youtube/pulls{/number}", - "pushed_at": "2024-03-25T20:07:51Z", - "releases_url": "https://api.github.com/repos/apify-projects/store-youtube/releases{/id}", - "size": 1222, - "squash_merge_commit_message": "COMMIT_MESSAGES", - "squash_merge_commit_title": "COMMIT_OR_PR_TITLE", - "ssh_url": "git@github.com:apify-projects/store-youtube.git", - "stargazers_count": 0, - "stargazers_url": "https://api.github.com/repos/apify-projects/store-youtube/stargazers", - "statuses_url": "https://api.github.com/repos/apify-projects/store-youtube/statuses/{sha}", - "subscribers_url": "https://api.github.com/repos/apify-projects/store-youtube/subscribers", - "subscription_url": "https://api.github.com/repos/apify-projects/store-youtube/subscription", - "svn_url": "https://github.com/apify-projects/store-youtube", - "tags_url": "https://api.github.com/repos/apify-projects/store-youtube/tags", - "teams_url": "https://api.github.com/repos/apify-projects/store-youtube/teams", - "topics": [], - "trees_url": "https://api.github.com/repos/apify-projects/store-youtube/git/trees{/sha}", - "updated_at": "2023-02-24T14:04:00Z", - "url": "https://api.github.com/repos/apify-projects/store-youtube", - "use_squash_pr_title_as_default": false, - "visibility": "private", - "watchers": 0, - "watchers_count": 0, - "web_commit_signoff_required": false - }, - "sha": "1aa5a14855943d8e5bb8706968de7b4b11fbd415", - "user": { - "avatar_url": "https://avatars.githubusercontent.com/u/69005150?v=4", - "events_url": "https://api.github.com/users/apify-projects/events{/privacy}", - "followers_url": "https://api.github.com/users/apify-projects/followers", - "following_url": "https://api.github.com/users/apify-projects/following{/other_user}", - "gists_url": "https://api.github.com/users/apify-projects/gists{/gist_id}", - "gravatar_id": "", - "html_url": "https://github.com/apify-projects", - "id": 69005150, - "login": "apify-projects", - "node_id": "MDEyOk9yZ2FuaXphdGlvbjY5MDA1MTUw", - "organizations_url": "https://api.github.com/users/apify-projects/orgs", - "received_events_url": "https://api.github.com/users/apify-projects/received_events", - "repos_url": "https://api.github.com/users/apify-projects/repos", - "site_admin": false, - "starred_url": "https://api.github.com/users/apify-projects/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/apify-projects/subscriptions", - "type": "Organization", - "url": "https://api.github.com/users/apify-projects" - } - }, - "body": "just an experiment, more [here](https://github.com/apify-projects/store-utils-actors/pull/10)", - "changed_files": 1, - "closed_at": null, - "comments": 0, - "comments_url": "https://api.github.com/repos/apify-projects/store-youtube/issues/136/comments", - "commits": 6, - "commits_url": "https://api.github.com/repos/apify-projects/store-youtube/pulls/136/commits", - "created_at": "2024-02-29T18:12:50Z", - "deletions": 20, - "diff_url": "https://github.com/apify-projects/store-youtube/pull/136.diff", - "draft": false, - "head": { - "label": "apify-projects:build/self-hosted-gh-runner", - "ref": "build/self-hosted-gh-runner", - "repo": { - "allow_auto_merge": false, - "allow_forking": true, - "allow_merge_commit": false, - "allow_rebase_merge": true, - "allow_squash_merge": true, - "allow_update_branch": true, - "archive_url": "https://api.github.com/repos/apify-projects/store-youtube/{archive_format}{/ref}", - "archived": false, - "assignees_url": "https://api.github.com/repos/apify-projects/store-youtube/assignees{/user}", - "blobs_url": "https://api.github.com/repos/apify-projects/store-youtube/git/blobs{/sha}", - "branches_url": "https://api.github.com/repos/apify-projects/store-youtube/branches{/branch}", - "clone_url": "https://github.com/apify-projects/store-youtube.git", - "collaborators_url": "https://api.github.com/repos/apify-projects/store-youtube/collaborators{/collaborator}", - "comments_url": "https://api.github.com/repos/apify-projects/store-youtube/comments{/number}", - "commits_url": "https://api.github.com/repos/apify-projects/store-youtube/commits{/sha}", - "compare_url": "https://api.github.com/repos/apify-projects/store-youtube/compare/{base}...{head}", - "contents_url": "https://api.github.com/repos/apify-projects/store-youtube/contents/{+path}", - "contributors_url": "https://api.github.com/repos/apify-projects/store-youtube/contributors", - "created_at": "2022-09-01T18:56:38Z", - "default_branch": "master", - "delete_branch_on_merge": true, - "deployments_url": "https://api.github.com/repos/apify-projects/store-youtube/deployments", - "description": null, - "disabled": false, - "downloads_url": "https://api.github.com/repos/apify-projects/store-youtube/downloads", - "events_url": "https://api.github.com/repos/apify-projects/store-youtube/events", - "fork": false, - "forks": 1, - "forks_count": 1, - "forks_url": "https://api.github.com/repos/apify-projects/store-youtube/forks", - "full_name": "apify-projects/store-youtube", - "git_commits_url": "https://api.github.com/repos/apify-projects/store-youtube/git/commits{/sha}", - "git_refs_url": "https://api.github.com/repos/apify-projects/store-youtube/git/refs{/sha}", - "git_tags_url": "https://api.github.com/repos/apify-projects/store-youtube/git/tags{/sha}", - "git_url": "git://github.com/apify-projects/store-youtube.git", - "has_discussions": false, - "has_downloads": true, - "has_issues": true, - "has_pages": false, - "has_projects": true, - "has_wiki": false, - "homepage": null, - "hooks_url": "https://api.github.com/repos/apify-projects/store-youtube/hooks", - "html_url": "https://github.com/apify-projects/store-youtube", - "id": 531649207, - "is_template": false, - "issue_comment_url": "https://api.github.com/repos/apify-projects/store-youtube/issues/comments{/number}", - "issue_events_url": "https://api.github.com/repos/apify-projects/store-youtube/issues/events{/number}", - "issues_url": "https://api.github.com/repos/apify-projects/store-youtube/issues{/number}", - "keys_url": "https://api.github.com/repos/apify-projects/store-youtube/keys{/key_id}", - "labels_url": "https://api.github.com/repos/apify-projects/store-youtube/labels{/name}", - "language": "TypeScript", - "languages_url": "https://api.github.com/repos/apify-projects/store-youtube/languages", - "license": { - "key": "apache-2.0", - "name": "Apache License 2.0", - "node_id": "MDc6TGljZW5zZTI=", - "spdx_id": "Apache-2.0", - "url": "https://api.github.com/licenses/apache-2.0" - }, - "merge_commit_message": "PR_TITLE", - "merge_commit_title": "MERGE_MESSAGE", - "merges_url": "https://api.github.com/repos/apify-projects/store-youtube/merges", - "milestones_url": "https://api.github.com/repos/apify-projects/store-youtube/milestones{/number}", - "mirror_url": null, - "name": "store-youtube", - "node_id": "R_kgDOH7BStw", - "notifications_url": "https://api.github.com/repos/apify-projects/store-youtube/notifications{?since,all,participating}", - "open_issues": 10, - "open_issues_count": 10, - "owner": { - "avatar_url": "https://avatars.githubusercontent.com/u/69005150?v=4", - "events_url": "https://api.github.com/users/apify-projects/events{/privacy}", - "followers_url": "https://api.github.com/users/apify-projects/followers", - "following_url": "https://api.github.com/users/apify-projects/following{/other_user}", - "gists_url": "https://api.github.com/users/apify-projects/gists{/gist_id}", - "gravatar_id": "", - "html_url": "https://github.com/apify-projects", - "id": 69005150, - "login": "apify-projects", - "node_id": "MDEyOk9yZ2FuaXphdGlvbjY5MDA1MTUw", - "organizations_url": "https://api.github.com/users/apify-projects/orgs", - "received_events_url": "https://api.github.com/users/apify-projects/received_events", - "repos_url": "https://api.github.com/users/apify-projects/repos", - "site_admin": false, - "starred_url": "https://api.github.com/users/apify-projects/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/apify-projects/subscriptions", - "type": "Organization", - "url": "https://api.github.com/users/apify-projects" - }, - "private": true, - "pulls_url": "https://api.github.com/repos/apify-projects/store-youtube/pulls{/number}", - "pushed_at": "2024-03-25T20:07:51Z", - "releases_url": "https://api.github.com/repos/apify-projects/store-youtube/releases{/id}", - "size": 1222, - "squash_merge_commit_message": "COMMIT_MESSAGES", - "squash_merge_commit_title": "COMMIT_OR_PR_TITLE", - "ssh_url": "git@github.com:apify-projects/store-youtube.git", - "stargazers_count": 0, - "stargazers_url": "https://api.github.com/repos/apify-projects/store-youtube/stargazers", - "statuses_url": "https://api.github.com/repos/apify-projects/store-youtube/statuses/{sha}", - "subscribers_url": "https://api.github.com/repos/apify-projects/store-youtube/subscribers", - "subscription_url": "https://api.github.com/repos/apify-projects/store-youtube/subscription", - "svn_url": "https://github.com/apify-projects/store-youtube", - "tags_url": "https://api.github.com/repos/apify-projects/store-youtube/tags", - "teams_url": "https://api.github.com/repos/apify-projects/store-youtube/teams", - "topics": [], - "trees_url": "https://api.github.com/repos/apify-projects/store-youtube/git/trees{/sha}", - "updated_at": "2023-02-24T14:04:00Z", - "url": "https://api.github.com/repos/apify-projects/store-youtube", - "use_squash_pr_title_as_default": false, - "visibility": "private", - "watchers": 0, - "watchers_count": 0, - "web_commit_signoff_required": false - }, - "sha": "c8b2b890bf0b2d2af80029f5ab02b6523c45c714", - "user": { - "avatar_url": "https://avatars.githubusercontent.com/u/69005150?v=4", - "events_url": "https://api.github.com/users/apify-projects/events{/privacy}", - "followers_url": "https://api.github.com/users/apify-projects/followers", - "following_url": "https://api.github.com/users/apify-projects/following{/other_user}", - "gists_url": "https://api.github.com/users/apify-projects/gists{/gist_id}", - "gravatar_id": "", - "html_url": "https://github.com/apify-projects", - "id": 69005150, - "login": "apify-projects", - "node_id": "MDEyOk9yZ2FuaXphdGlvbjY5MDA1MTUw", - "organizations_url": "https://api.github.com/users/apify-projects/orgs", - "received_events_url": "https://api.github.com/users/apify-projects/received_events", - "repos_url": "https://api.github.com/users/apify-projects/repos", - "site_admin": false, - "starred_url": "https://api.github.com/users/apify-projects/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/apify-projects/subscriptions", - "type": "Organization", - "url": "https://api.github.com/users/apify-projects" - } - }, - "html_url": "https://github.com/apify-projects/store-youtube/pull/136", - "id": 1750285021, - "issue_url": "https://api.github.com/repos/apify-projects/store-youtube/issues/136", - "labels": [], - "locked": false, - "maintainer_can_modify": false, - "merge_commit_sha": "60a4ba98d2a9c52341a0432e7663889629f446e8", - "mergeable": null, - "mergeable_state": "unknown", - "merged": false, - "merged_at": null, - "merged_by": null, - "milestone": null, - "node_id": "PR_kwDOH7BSt85oUzrd", - "number": 136, - "patch_url": "https://github.com/apify-projects/store-youtube/pull/136.patch", - "rebaseable": null, - "requested_reviewers": [], - "requested_teams": [], - "review_comment_url": "https://api.github.com/repos/apify-projects/store-youtube/pulls/comments{/number}", - "review_comments": 0, - "review_comments_url": "https://api.github.com/repos/apify-projects/store-youtube/pulls/136/comments", - "state": "open", - "statuses_url": "https://api.github.com/repos/apify-projects/store-youtube/statuses/c8b2b890bf0b2d2af80029f5ab02b6523c45c714", - "title": "WIP: build: add self-hosted GH runner", - "updated_at": "2024-03-25T20:07:51Z", - "url": "https://api.github.com/repos/apify-projects/store-youtube/pulls/136", - "user": { - "avatar_url": "https://avatars.githubusercontent.com/u/31389543?v=4", - "events_url": "https://api.github.com/users/oklinov/events{/privacy}", - "followers_url": "https://api.github.com/users/oklinov/followers", - "following_url": "https://api.github.com/users/oklinov/following{/other_user}", - "gists_url": "https://api.github.com/users/oklinov/gists{/gist_id}", - "gravatar_id": "", - "html_url": "https://github.com/oklinov", - "id": 31389543, - "login": "oklinov", - "node_id": "MDQ6VXNlcjMxMzg5NTQz", - "organizations_url": "https://api.github.com/users/oklinov/orgs", - "received_events_url": "https://api.github.com/users/oklinov/received_events", - "repos_url": "https://api.github.com/users/oklinov/repos", - "site_admin": false, - "starred_url": "https://api.github.com/users/oklinov/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/oklinov/subscriptions", - "type": "User", - "url": "https://api.github.com/users/oklinov" - } - }, - "repository": { - "allow_forking": true, - "archive_url": "https://api.github.com/repos/apify-projects/store-youtube/{archive_format}{/ref}", - "archived": false, - "assignees_url": "https://api.github.com/repos/apify-projects/store-youtube/assignees{/user}", - "blobs_url": "https://api.github.com/repos/apify-projects/store-youtube/git/blobs{/sha}", - "branches_url": "https://api.github.com/repos/apify-projects/store-youtube/branches{/branch}", - "clone_url": "https://github.com/apify-projects/store-youtube.git", - "collaborators_url": "https://api.github.com/repos/apify-projects/store-youtube/collaborators{/collaborator}", - "comments_url": "https://api.github.com/repos/apify-projects/store-youtube/comments{/number}", - "commits_url": "https://api.github.com/repos/apify-projects/store-youtube/commits{/sha}", - "compare_url": "https://api.github.com/repos/apify-projects/store-youtube/compare/{base}...{head}", - "contents_url": "https://api.github.com/repos/apify-projects/store-youtube/contents/{+path}", - "contributors_url": "https://api.github.com/repos/apify-projects/store-youtube/contributors", - "created_at": "2022-09-01T18:56:38Z", - "custom_properties": {}, - "default_branch": "master", - "deployments_url": "https://api.github.com/repos/apify-projects/store-youtube/deployments", - "description": null, - "disabled": false, - "downloads_url": "https://api.github.com/repos/apify-projects/store-youtube/downloads", - "events_url": "https://api.github.com/repos/apify-projects/store-youtube/events", - "fork": false, - "forks": 1, - "forks_count": 1, - "forks_url": "https://api.github.com/repos/apify-projects/store-youtube/forks", - "full_name": "apify-projects/store-youtube", - "git_commits_url": "https://api.github.com/repos/apify-projects/store-youtube/git/commits{/sha}", - "git_refs_url": "https://api.github.com/repos/apify-projects/store-youtube/git/refs{/sha}", - "git_tags_url": "https://api.github.com/repos/apify-projects/store-youtube/git/tags{/sha}", - "git_url": "git://github.com/apify-projects/store-youtube.git", - "has_discussions": false, - "has_downloads": true, - "has_issues": true, - "has_pages": false, - "has_projects": true, - "has_wiki": false, - "homepage": null, - "hooks_url": "https://api.github.com/repos/apify-projects/store-youtube/hooks", - "html_url": "https://github.com/apify-projects/store-youtube", - "id": 531649207, - "is_template": false, - "issue_comment_url": "https://api.github.com/repos/apify-projects/store-youtube/issues/comments{/number}", - "issue_events_url": "https://api.github.com/repos/apify-projects/store-youtube/issues/events{/number}", - "issues_url": "https://api.github.com/repos/apify-projects/store-youtube/issues{/number}", - "keys_url": "https://api.github.com/repos/apify-projects/store-youtube/keys{/key_id}", - "labels_url": "https://api.github.com/repos/apify-projects/store-youtube/labels{/name}", - "language": "TypeScript", - "languages_url": "https://api.github.com/repos/apify-projects/store-youtube/languages", - "license": { - "key": "apache-2.0", - "name": "Apache License 2.0", - "node_id": "MDc6TGljZW5zZTI=", - "spdx_id": "Apache-2.0", - "url": "https://api.github.com/licenses/apache-2.0" - }, - "merges_url": "https://api.github.com/repos/apify-projects/store-youtube/merges", - "milestones_url": "https://api.github.com/repos/apify-projects/store-youtube/milestones{/number}", - "mirror_url": null, - "name": "store-youtube", - "node_id": "R_kgDOH7BStw", - "notifications_url": "https://api.github.com/repos/apify-projects/store-youtube/notifications{?since,all,participating}", - "open_issues": 10, - "open_issues_count": 10, - "owner": { - "avatar_url": "https://avatars.githubusercontent.com/u/69005150?v=4", - "events_url": "https://api.github.com/users/apify-projects/events{/privacy}", - "followers_url": "https://api.github.com/users/apify-projects/followers", - "following_url": "https://api.github.com/users/apify-projects/following{/other_user}", - "gists_url": "https://api.github.com/users/apify-projects/gists{/gist_id}", - "gravatar_id": "", - "html_url": "https://github.com/apify-projects", - "id": 69005150, - "login": "apify-projects", - "node_id": "MDEyOk9yZ2FuaXphdGlvbjY5MDA1MTUw", - "organizations_url": "https://api.github.com/users/apify-projects/orgs", - "received_events_url": "https://api.github.com/users/apify-projects/received_events", - "repos_url": "https://api.github.com/users/apify-projects/repos", - "site_admin": false, - "starred_url": "https://api.github.com/users/apify-projects/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/apify-projects/subscriptions", - "type": "Organization", - "url": "https://api.github.com/users/apify-projects" - }, - "private": true, - "pulls_url": "https://api.github.com/repos/apify-projects/store-youtube/pulls{/number}", - "pushed_at": "2024-03-25T20:07:51Z", - "releases_url": "https://api.github.com/repos/apify-projects/store-youtube/releases{/id}", - "size": 1222, - "ssh_url": "git@github.com:apify-projects/store-youtube.git", - "stargazers_count": 0, - "stargazers_url": "https://api.github.com/repos/apify-projects/store-youtube/stargazers", - "statuses_url": "https://api.github.com/repos/apify-projects/store-youtube/statuses/{sha}", - "subscribers_url": "https://api.github.com/repos/apify-projects/store-youtube/subscribers", - "subscription_url": "https://api.github.com/repos/apify-projects/store-youtube/subscription", - "svn_url": "https://github.com/apify-projects/store-youtube", - "tags_url": "https://api.github.com/repos/apify-projects/store-youtube/tags", - "teams_url": "https://api.github.com/repos/apify-projects/store-youtube/teams", - "topics": [], - "trees_url": "https://api.github.com/repos/apify-projects/store-youtube/git/trees{/sha}", - "updated_at": "2023-02-24T14:04:00Z", - "url": "https://api.github.com/repos/apify-projects/store-youtube", - "visibility": "private", - "watchers": 0, - "watchers_count": 0, - "web_commit_signoff_required": false - }, - "sender": { - "avatar_url": "https://avatars.githubusercontent.com/u/31389543?v=4", - "events_url": "https://api.github.com/users/oklinov/events{/privacy}", - "followers_url": "https://api.github.com/users/oklinov/followers", - "following_url": "https://api.github.com/users/oklinov/following{/other_user}", - "gists_url": "https://api.github.com/users/oklinov/gists{/gist_id}", - "gravatar_id": "", - "html_url": "https://github.com/oklinov", - "id": 31389543, - "login": "oklinov", - "node_id": "MDQ6VXNlcjMxMzg5NTQz", - "organizations_url": "https://api.github.com/users/oklinov/orgs", - "received_events_url": "https://api.github.com/users/oklinov/received_events", - "repos_url": "https://api.github.com/users/oklinov/repos", - "site_admin": false, - "starred_url": "https://api.github.com/users/oklinov/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/oklinov/subscriptions", - "type": "User", - "url": "https://api.github.com/users/oklinov" - } -} diff --git a/example-github-events/push.json b/example-github-events/push.json deleted file mode 100644 index 0f89131..0000000 --- a/example-github-events/push.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "ref": "refs/heads/master", - "before": "e15bf2a167bec5681afef2a5c057b8a10b78850d", - "after": "7f9e105e4ce7881f9def76db60bd8e4e58ead9f2", - "repository": { - "id": 530628540, - "node_id": "R_kgDOH6C_vA", - "name": "store-booking", - "full_name": "apify-projects/store-booking", - "private": true, - "owner": { - "name": "apify-projects", - "email": "hello@apify.com", - "login": "apify-projects", - "id": 69005150, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjY5MDA1MTUw", - "avatar_url": "https://avatars.githubusercontent.com/u/69005150?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/apify-projects", - "html_url": "https://github.com/apify-projects", - "followers_url": "https://api.github.com/users/apify-projects/followers", - "following_url": "https://api.github.com/users/apify-projects/following{/other_user}", - "gists_url": "https://api.github.com/users/apify-projects/gists{/gist_id}", - "starred_url": "https://api.github.com/users/apify-projects/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/apify-projects/subscriptions", - "organizations_url": "https://api.github.com/users/apify-projects/orgs", - "repos_url": "https://api.github.com/users/apify-projects/repos", - "events_url": "https://api.github.com/users/apify-projects/events{/privacy}", - "received_events_url": "https://api.github.com/users/apify-projects/received_events", - "type": "Organization", - "site_admin": false - }, - "html_url": "https://github.com/apify-projects/store-booking", - "description": null, - "fork": false, - "url": "https://github.com/apify-projects/store-booking", - "forks_url": "https://api.github.com/repos/apify-projects/store-booking/forks", - "keys_url": "https://api.github.com/repos/apify-projects/store-booking/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/apify-projects/store-booking/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/apify-projects/store-booking/teams", - "hooks_url": "https://api.github.com/repos/apify-projects/store-booking/hooks", - "issue_events_url": "https://api.github.com/repos/apify-projects/store-booking/issues/events{/number}", - "events_url": "https://api.github.com/repos/apify-projects/store-booking/events", - "assignees_url": "https://api.github.com/repos/apify-projects/store-booking/assignees{/user}", - "branches_url": "https://api.github.com/repos/apify-projects/store-booking/branches{/branch}", - "tags_url": "https://api.github.com/repos/apify-projects/store-booking/tags", - "blobs_url": "https://api.github.com/repos/apify-projects/store-booking/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/apify-projects/store-booking/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/apify-projects/store-booking/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/apify-projects/store-booking/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/apify-projects/store-booking/statuses/{sha}", - "languages_url": "https://api.github.com/repos/apify-projects/store-booking/languages", - "stargazers_url": "https://api.github.com/repos/apify-projects/store-booking/stargazers", - "contributors_url": "https://api.github.com/repos/apify-projects/store-booking/contributors", - "subscribers_url": "https://api.github.com/repos/apify-projects/store-booking/subscribers", - "subscription_url": "https://api.github.com/repos/apify-projects/store-booking/subscription", - "commits_url": "https://api.github.com/repos/apify-projects/store-booking/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/apify-projects/store-booking/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/apify-projects/store-booking/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/apify-projects/store-booking/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/apify-projects/store-booking/contents/{+path}", - "compare_url": "https://api.github.com/repos/apify-projects/store-booking/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/apify-projects/store-booking/merges", - "archive_url": "https://api.github.com/repos/apify-projects/store-booking/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/apify-projects/store-booking/downloads", - "issues_url": "https://api.github.com/repos/apify-projects/store-booking/issues{/number}", - "pulls_url": "https://api.github.com/repos/apify-projects/store-booking/pulls{/number}", - "milestones_url": "https://api.github.com/repos/apify-projects/store-booking/milestones{/number}", - "notifications_url": "https://api.github.com/repos/apify-projects/store-booking/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/apify-projects/store-booking/labels{/name}", - "releases_url": "https://api.github.com/repos/apify-projects/store-booking/releases{/id}", - "deployments_url": "https://api.github.com/repos/apify-projects/store-booking/deployments", - "created_at": 1661859371, - "updated_at": "2024-07-01T22:15:13Z", - "pushed_at": 1721436715, - "git_url": "git://github.com/apify-projects/store-booking.git", - "ssh_url": "git@github.com:apify-projects/store-booking.git", - "clone_url": "https://github.com/apify-projects/store-booking.git", - "svn_url": "https://github.com/apify-projects/store-booking", - "homepage": "", - "size": 3080, - "stargazers_count": 0, - "watchers_count": 0, - "language": "TypeScript", - "has_issues": true, - "has_projects": true, - "has_downloads": true, - "has_wiki": true, - "has_pages": false, - "has_discussions": false, - "forks_count": 1, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 19, - "license": { - "key": "apache-2.0", - "name": "Apache License 2.0", - "spdx_id": "Apache-2.0", - "url": "https://api.github.com/licenses/apache-2.0", - "node_id": "MDc6TGljZW5zZTI=" - }, - "allow_forking": true, - "is_template": false, - "web_commit_signoff_required": false, - "topics": [], - "visibility": "private", - "forks": 1, - "open_issues": 19, - "watchers": 0, - "default_branch": "master", - "stargazers": 0, - "master_branch": "master", - "organization": "apify-projects", - "custom_properties": {} - }, - "pusher": { - "name": "lhotanok", - "email": "84460709+lhotanok@users.noreply.github.com" - }, - "organization": { - "login": "apify-projects", - "id": 69005150, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjY5MDA1MTUw", - "url": "https://api.github.com/orgs/apify-projects", - "repos_url": "https://api.github.com/orgs/apify-projects/repos", - "events_url": "https://api.github.com/orgs/apify-projects/events", - "hooks_url": "https://api.github.com/orgs/apify-projects/hooks", - "issues_url": "https://api.github.com/orgs/apify-projects/issues", - "members_url": "https://api.github.com/orgs/apify-projects/members{/member}", - "public_members_url": "https://api.github.com/orgs/apify-projects/public_members{/member}", - "avatar_url": "https://avatars.githubusercontent.com/u/69005150?v=4", - "description": "This organization houses projects that are not part of Apify itself, such as enterprise or marketplace developed actors and scrapers." - }, - "sender": { - "login": "lhotanok", - "id": 84460709, - "node_id": "MDQ6VXNlcjg0NDYwNzA5", - "avatar_url": "https://avatars.githubusercontent.com/u/84460709?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/lhotanok", - "html_url": "https://github.com/lhotanok", - "followers_url": "https://api.github.com/users/lhotanok/followers", - "following_url": "https://api.github.com/users/lhotanok/following{/other_user}", - "gists_url": "https://api.github.com/users/lhotanok/gists{/gist_id}", - "starred_url": "https://api.github.com/users/lhotanok/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/lhotanok/subscriptions", - "organizations_url": "https://api.github.com/users/lhotanok/orgs", - "repos_url": "https://api.github.com/users/lhotanok/repos", - "events_url": "https://api.github.com/users/lhotanok/events{/privacy}", - "received_events_url": "https://api.github.com/users/lhotanok/received_events", - "type": "User", - "site_admin": false - }, - "created": false, - "deleted": false, - "forced": false, - "base_ref": null, - "compare": "https://github.com/apify-projects/store-booking/compare/6b186d1fa8b6...591b7f55c7b8", - "commits": [ - { - "id": "7f9e105e4ce7881f9def76db60bd8e4e58ead9f2", - "tree_id": "3f16d04f6d1cb44f8b90015a69168fdcc29621e1", - "distinct": true, - "message": "fix: A", - "timestamp": "2024-07-20T02:51:54+02:00", - "url": "https://github.com/apify-projects/store-booking/commit/591b7f55c7b8feb5012937f870d43266b0650118", - "author": { - "name": "Kristýna Lhoťanová", - "email": "84460709+lhotanok@users.noreply.github.com", - "username": "oklinov" - }, - "committer": { - "name": "GitHub", - "email": "noreply@github.com", - "username": "web-flow" - } - }, - { - "id": "3d1556141893eb3b7f281702e5cbec13d8814156", - "tree_id": "3f16d04f6d1cb44f8b90015a69168fdcc29621e1", - "distinct": true, - "message": "feat: A", - "timestamp": "2024-07-20T02:51:54+02:00", - "url": "https://github.com/apify-projects/store-booking/commit/591b7f55c7b8feb5012937f870d43266b0650118", - "author": { - "name": "Kristýna Lhoťanová", - "email": "84460709+lhotanok@users.noreply.github.com", - "username": "oklinov" - }, - "committer": { - "name": "GitHub", - "email": "noreply@github.com", - "username": "web-flow" - } - } - ], - "head_commit": { - "id": "591b7f55c7b8feb5012937f870d43266b0650118", - "tree_id": "3f16d04f6d1cb44f8b90015a69168fdcc29621e1", - "distinct": true, - "message": "fix: validate rendered detail page locale (both language & currency) (#133)", - "timestamp": "2024-07-20T02:51:54+02:00", - "url": "https://github.com/apify-projects/store-booking/commit/591b7f55c7b8feb5012937f870d43266b0650118", - "author": { - "name": "Kristýna Lhoťanová", - "email": "84460709+lhotanok@users.noreply.github.com", - "username": "lhotanok" - }, - "committer": { - "name": "GitHub", - "email": "noreply@github.com", - "username": "web-flow" - } - } - } diff --git a/knip.json b/knip.json index 3aea74a..84d4d08 100644 --- a/knip.json +++ b/knip.json @@ -1,3 +1,3 @@ { "$schema": "https://unpkg.com/knip@5/schema.json" -} \ No newline at end of file +} diff --git a/lib/extend-expect.ts b/lib/extend-expect.ts index 95d902d..7995066 100644 --- a/lib/extend-expect.ts +++ b/lib/extend-expect.ts @@ -1,8 +1,8 @@ import type { Assertion, ExpectStatic } from 'vitest'; import { TO_FINISH_WITH_OPTIONS } from './consts.js'; +import type { RunTestResult } from './run-test-result.js'; import type { Interval, ToFinishWithOptions } from './types.js'; -import { RunTestResult } from './run-test-result.js'; export const extendExpect = (expect: ExpectStatic): ExpectStatic => { expect.extend({ @@ -110,7 +110,7 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => { }, toFinishWith: async ( received: RunTestResult, - userOptions: ToFinishWithOptions + userOptions: ToFinishWithOptions, ) => { const options = { ...TO_FINISH_WITH_OPTIONS, @@ -146,12 +146,12 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => { const checkInterval = ( value: number | undefined, key: ['datasetItemCount', 'duration', 'failedRequests', 'requestsRetries'][number], - label: string + label: string, ) => { const result = isWithinInterval(diffs, value, options, key); if (result === false) { failedAssertions.push( - `Failed ${label} check, expected ${JSON.stringify(options[key])}, got ${value}.` + `Failed ${label} check, expected ${JSON.stringify(options[key])}, got ${value}.`, ); } }; @@ -161,17 +161,15 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => { checkInterval(stats?.requestsFailed, 'failedRequests', 'failed requests'); checkInterval(stats?.requestsRetries, 'requestsRetries', 'requests retries'); - { - if (options.maxRetriesPerRequest !== null) { - const maxRetriesPerRequestObserved = (stats?.requestRetryHistogram ?? [0]).length - 1; - if (maxRetriesPerRequestObserved > options.maxRetriesPerRequest) { - diffs.pass = false; - diffs.actual.push(`maxRetriesPerRequest=${maxRetriesPerRequestObserved}`); - diffs.expected.push(`maxRetriesPerRequest<=${options.maxRetriesPerRequest}`); - failedAssertions.push( - `Failed max retries observed check, expected <=${options.maxRetriesPerRequest}, got ${maxRetriesPerRequestObserved}.` - ); - } + if (options.maxRetriesPerRequest !== null) { + const maxRetriesPerRequestObserved = (stats?.requestRetryHistogram ?? [0]).length - 1; + if (maxRetriesPerRequestObserved > options.maxRetriesPerRequest) { + diffs.pass = false; + diffs.actual.push(`maxRetriesPerRequest=${maxRetriesPerRequestObserved}`); + diffs.expected.push(`maxRetriesPerRequest<=${options.maxRetriesPerRequest}`); + failedAssertions.push( + `Failed max retries observed check, expected <=${options.maxRetriesPerRequest}, got ${maxRetriesPerRequestObserved}.`, + ); } } const ppeDiffs: Diffs = { @@ -208,8 +206,8 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => { diffs.expected.push(ppeDiffs.expected.join('\n ')); failedAssertions.push( `Failed PPE event counts check, expected ${JSON.stringify( - options.chargedEventCounts - )}, got ${JSON.stringify(chargedEventCounts)}.` + options.chargedEventCounts, + )}, got ${JSON.stringify(chargedEventCounts)}.`, ); } } @@ -228,7 +226,7 @@ export const extendExpect = (expect: ExpectStatic): ExpectStatic => { diffs.actual.push(` logs=[${occuredLogs.join(', ')}]`); diffs.expected.push(` logs=[]`); failedAssertions.push( - `Failed forbidden logs check, expected [] but got [${occuredLogs.join(', ')}].` + `Failed forbidden logs check, expected [] but got [${occuredLogs.join(', ')}].`, ); } } @@ -280,28 +278,32 @@ const isWithinInterval = ( diffs: Diffs, actual: number | undefined, options: Record, - intervalOption: T + intervalOption: T, ) => { const expected = options[intervalOption]; if (expected === null) { // check is disabled if expected value is null - return; + return undefined; } if (typeof expected === 'number') { if (actual !== expected) { + // eslint-disable-next-line no-param-reassign --- Don't want to touch this code :) diffs.pass = false; diffs.actual.push(`${intervalOption}=${actual}`); diffs.expected.push(`${intervalOption}=${expected}`); + // eslint-disable-next-line consistent-return return false; } } else if (typeof expected === 'object') { const { min, max } = expected; if (actual === undefined || (min !== undefined && actual < min) || (max !== undefined && actual > max)) { + // eslint-disable-next-line no-param-reassign diffs.pass = false; diffs.actual.push(`${intervalOption}=${actual}`); diffs.expected.push(`${intervalOption}=<${min ?? ''},${max ?? ''}>`); + // eslint-disable-next-line consistent-return return false; } } - return; + return undefined; }; diff --git a/lib/lib.ts b/lib/lib.ts index 532fa98..1702740 100644 --- a/lib/lib.ts +++ b/lib/lib.ts @@ -1,15 +1,11 @@ -import { Actor, ActorRun, ActorRunListItem, ActorStandby, ApifyClient, Task } from 'apify-client'; -import { - describe as vitestDescribe, - ExpectStatic, - TestFunction, - test as vitestTest, - SuiteFactory, - TestContext, -} from 'vitest'; -import type { ActorBuild, ActorTestOptions, RunOptions } from './types.js'; -import { RunTestResult } from './run-test-result.js'; +import type { Actor, ActorRun, ActorRunListItem, ActorStandby, Task } from 'apify-client'; +import { ApifyClient } from 'apify-client'; +import type { SuiteFactory, TestContext, TestFunction } from 'vitest'; +import { describe as vitestDescribe, ExpectStatic, test as vitestTest } from 'vitest'; + import { extendExpect } from './extend-expect.js'; +import { RunTestResult } from './run-test-result.js'; +import type { ActorBuild, ActorTestOptions, RunOptions } from './types.js'; import { getActorPrefilledInput, sleep } from './utils.js'; const ACTOR_BUILDS = 'ACTOR_BUILDS'; @@ -56,7 +52,7 @@ export const testActor = ( actorName: string, testName: string, fn: TestFunction<{ run: ReturnType> }>, - testOptions?: ActorTestOptions + testOptions?: ActorTestOptions, ) => { const options = { ...DEFAULT_TEST_ACTOR_OPTIONS, @@ -64,7 +60,7 @@ export const testActor = ( }; const name = `${actorName}: ${testName}`; const shouldRun = !!RUN_ALL_PLATFORM_TESTS || config.has(actorName); - vitestTest.runIf(shouldRun)(name, options, async (context: T) => { + vitestTest.runIf(shouldRun)(name, options, async (context: TYPE) => { const { expect, ...rest } = context; await fn({ expect: extendExpect(expect), @@ -80,11 +76,12 @@ export const testActor = ( * * Using task is just current shortcoming of standby feature but ideally we would use Actor directly */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const testStandbyActor = ( actorName: string, testName: string, fn: TestFunction<{ callStandby: ReturnType> }>, - testOptions?: ActorTestOptions + testOptions?: ActorTestOptions, ) => { const options = { ...DEFAULT_TEST_ACTOR_OPTIONS, @@ -105,7 +102,9 @@ export const testStandbyActor = ( callStandby: createStartStandbyFn(standbyTask), ...rest, }); - } catch {} + } catch { + /* */ + } const { taskId } = standbyTask; const runs = (await apifyClient.task(taskId).runs().list()).items; @@ -122,13 +121,14 @@ export const testStandbyActor = ( export const testTestActor = ( testName: string, - fn: TestFunction<{ run: ReturnType> }> + fn: TestFunction<{ run: ReturnType> }>, ) => { vitestTest(testName, async (context) => { const { expect, ...rest } = context; await fn({ expect: extendExpect(expect), // @ts-expect-error: this just to test custom matchers + // eslint-disable-next-line @typescript-eslint/no-empty-function run: () => {}, ...rest, }); @@ -191,7 +191,7 @@ const createStandbyTask = async (actorNameOrId: string, buildNumber?: string): P throw new Error(`Actor "${actorNameOrId} doesn't contain actorStandby options`); } const { isEnabled, ...defaultActorStandby } = actorInfo.actorStandby; - delete defaultActorStandby.disableStandbyFieldsOverride + delete defaultActorStandby.disableStandbyFieldsOverride; const build = buildNumber ?? defaultActorStandby.build; diff --git a/lib/run-test-result.ts b/lib/run-test-result.ts index a15d266..7083459 100644 --- a/lib/run-test-result.ts +++ b/lib/run-test-result.ts @@ -1,4 +1,5 @@ -import { ActorRun, ApifyClient, KeyValueStoreClient } from 'apify-client'; +import type { ActorRun, ApifyClient, KeyValueStoreClient } from 'apify-client'; + import type { Dataset, SdkCrawlerStatistics } from './types.js'; export class RunTestResult { @@ -9,7 +10,10 @@ export class RunTestResult { private runInfo: ActorRun | undefined; private input: unknown | undefined; - constructor(private readonly apifyClient: ApifyClient, private readonly run: ActorRun) { + constructor( + private readonly apifyClient: ApifyClient, + private readonly run: ActorRun, + ) { /**/ } diff --git a/lib/types.ts b/lib/types.ts index f42db87..8557d78 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,5 +1,5 @@ -import { ActorCallOptions } from 'apify-client'; -import { Assertion, TestOptions } from 'vitest'; +import type { ActorCallOptions } from 'apify-client'; +import type { Assertion, TestOptions } from 'vitest'; export type ActorBuild = { buildId: string; @@ -50,10 +50,6 @@ export type ToFinishWithOptionsWithDefaults = { forbiddenLogs: string[]; maxRetriesPerRequest: number | null; }; -export type IntervalOption = keyof Pick< - ToFinishWithOptions, - 'datasetItemCount' | 'requestsRetries' | 'failedRequests' | 'duration' ->; export type ToFinishWithOptions = Partial & { datasetItemCount: Interval; @@ -144,9 +140,10 @@ export type ActorTestOptions = Omit & { }; declare module 'vitest' { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type interface Assertion extends ActorMatchers {} - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type interface Matchers extends ActorMatchers {} + // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface AsymmetricMatchersContaining extends ActorMatchers {} } diff --git a/lib/utils.ts b/lib/utils.ts index 62a9d8a..0034cb2 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,5 +1,4 @@ -import { ActorRun, ApifyClient } from 'apify-client'; -import { TestContext } from 'vitest'; +import type { ApifyClient } from 'apify-client'; /** * Gets prefilled values for a provided build or, if not provided, uses actor's @@ -16,26 +15,29 @@ export const getActorPrefilledInput = async ( const defaultBuildTag = actorInfo?.defaultRunOptions.build; const taggedBuild = actorInfo?.taggedBuilds?.[defaultBuildTag || '']; + // eslint-disable-next-line no-param-reassign --- I think here it is cleaner than creating dummy variable buildId = taggedBuild?.buildId; if (!buildId) { console.error(`Coudn't find default build for actor ${actorNameOrId}. Prefilled values will not be used.`); - return {} + return {}; } } const buildInfo = await apifyClient.build(buildId).get(); - const inputSchema = buildInfo?.actorDefinition?.input as { - properties: Record - } | undefined + const inputSchema = buildInfo?.actorDefinition?.input as + | { + properties: Record; + } + | undefined; if (!inputSchema) { console.error( `Coudn't find input schema definition for actor ${actorNameOrId}, build ${buildId}.`, 'Prefilled values will not be used', ); - return {} + return {}; } const prefill: Record = {}; @@ -50,5 +52,7 @@ export const getActorPrefilledInput = async ( }; export const sleep = async (ms: number) => { - await new Promise((resolve) => setTimeout(resolve, ms)); -} + await new Promise((resolve) => { + setTimeout(resolve, ms); + }); +}; diff --git a/package-lock.json b/package-lock.json index 1d5b875..0c0583d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,9 @@ "eslint": "^9.29.0", "eslint-config-prettier": "^10.1.5", "globals": "^17.0.0", + "husky": "^9.0.11", "knip": "^5.65.0", + "lint-staged": "^15.2.2", "prettier": "^3.5.3", "tsx": "^4.20.3", "typescript": "^5.9.3", @@ -2215,6 +2217,34 @@ "node": ">=6" } }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2595,45 +2625,51 @@ "node": ">= 16" } }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=20" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", "dependencies": { - "ansi-regex": "^6.0.1" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=20" } }, "node_modules/color-convert": { @@ -2656,6 +2692,13 @@ "dev": true, "license": "MIT" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2668,6 +2711,16 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2904,6 +2957,19 @@ "node": ">= 0.4" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/es-abstract": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", @@ -3556,6 +3622,43 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "license": "MIT" }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -3892,6 +3995,19 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -4109,6 +4225,32 @@ "node": ">= 14" } }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4337,6 +4479,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -4740,6 +4895,78 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lint-staged": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", + "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4770,6 +4997,72 @@ "dev": true, "license": "MIT" }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -4805,6 +5098,13 @@ "node": ">= 0.4" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4863,6 +5163,32 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -4914,6 +5240,35 @@ "node": ">= 0.4.0" } }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -5027,6 +5382,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5311,6 +5682,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -5517,6 +5901,39 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -5537,6 +5954,13 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rollup": { "version": "4.53.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", @@ -5826,6 +6250,49 @@ "dev": true, "license": "ISC" }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -5925,6 +6392,16 @@ "node": ">= 0.4" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -5942,39 +6419,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/string-width/node_modules/emoji-regex": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT" }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/string.prototype.trim": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", @@ -6034,6 +6484,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -6044,6 +6509,19 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -6984,18 +7462,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", @@ -7008,21 +7474,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 2250fd5..18e1f03 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,9 @@ "typescript": "^5.9.3", "typescript-eslint": "^8.34.1", "vitest": "^3.2.4", - "knip": "^5.65.0" + "knip": "^5.65.0", + "husky": "^9.0.11", + "lint-staged": "^15.2.2" }, "peerDependencies": { "vitest": ">=3.2.4" @@ -49,7 +51,12 @@ "format:check": "prettier --check .", "type-check": "tsc --noEmit", "test": "vitest run", - "check-unused": "npx knip" + "check-unused": "npx knip", + "prepare": "husky .husky || true" + }, + "lint-staged": { + "*.js": "eslint", + "*.ts": "eslint" }, "author": "oklinov", "license": "ISC" diff --git a/test/tsconfig.json b/test/tsconfig.json index 830afec..50515ad 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -7,9 +7,7 @@ "noUnusedLocals": false, "noEmit": true, "lib": ["DOM", "es2021"], - "skipLibCheck": true, + "skipLibCheck": true }, - "include": [ - "./**/*", - ] + "include": ["./**/*"] } diff --git a/test/unit/bin/diff-changes.test.ts b/test/unit/bin/diff-changes.test.ts index 7ece048..727c366 100644 --- a/test/unit/bin/diff-changes.test.ts +++ b/test/unit/bin/diff-changes.test.ts @@ -5,18 +5,28 @@ import * as DiffJsonSchema from '../../../bin/diff-json-schema.js'; import type { ActorConfig } from '../../../bin/types.js'; const miniActor: ActorConfig = { actorName: 'foo/bar', folder: 'actors/foo_bar', isStandalone: false }; -const standaloneActor: ActorConfig = { actorName: 'standalone', folder: 'standalone-actors/standalone', isStandalone: true }; +const standaloneActor: ActorConfig = { + actorName: 'standalone', + folder: 'standalone-actors/standalone', + isStandalone: true, +}; const actorConfigs = [miniActor, standaloneActor]; const commits = [{ sha: 'Commit1', author: '', date: '', message: '' }]; describe('maybeParseActorFolder', () => { it('returns actorName for actors/ path', () => { - expect(maybeParseActorFolder('actors/foo_bar/actor.json')).toEqual({ isActorFolder: true, actorName: 'foo/bar' }); + expect(maybeParseActorFolder('actors/foo_bar/actor.json')).toEqual({ + isActorFolder: true, + actorName: 'foo/bar', + }); }); it('returns actorName for standalone-actors/ path', () => { - expect(maybeParseActorFolder('standalone-actors/my_actor/main.ts')).toEqual({ isActorFolder: true, actorName: 'my/actor' }); + expect(maybeParseActorFolder('standalone-actors/my_actor/main.ts')).toEqual({ + isActorFolder: true, + actorName: 'my/actor', + }); }); it('returns false for top-level file', () => { @@ -153,10 +163,7 @@ describe('getChangedActors', () => { it('handles mixed changes: returns both mini and standalone actors', () => { const result = getChangedActors({ - filepathsChanged: [ - 'actors/foo_bar/src/main.ts', - 'standalone-actors/standalone/Dockerfile', - ], + filepathsChanged: ['actors/foo_bar/src/main.ts', 'standalone-actors/standalone/Dockerfile'], actorConfigs, commits, }); diff --git a/test/unit/bin/diff.test.ts b/test/unit/bin/diff.test.ts index 3bafed2..e5ade40 100644 --- a/test/unit/bin/diff.test.ts +++ b/test/unit/bin/diff.test.ts @@ -17,9 +17,7 @@ describe('isCosmeticOnlyJsonSchemaChange', () => { }); const mockGitCalls = (oldJson: string, newJson: string) => { - gitCommandSpy - .mockReturnValueOnce(oldJson) - .mockReturnValueOnce(newJson); + gitCommandSpy.mockReturnValueOnce(oldJson).mockReturnValueOnce(newJson); }; test('returns true when nothing changed', () => { @@ -69,10 +67,7 @@ describe('isCosmeticOnlyJsonSchemaChange', () => { }); test('returns true when only sectionDescription changes', () => { - mockGitCalls( - JSON.stringify({ sectionDescription: 'Old' }), - JSON.stringify({ sectionDescription: 'New' }), - ); + mockGitCalls(JSON.stringify({ sectionDescription: 'Old' }), JSON.stringify({ sectionDescription: 'New' })); expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true); }); @@ -85,18 +80,12 @@ describe('isCosmeticOnlyJsonSchemaChange', () => { }); test('returns false when a functional field is added', () => { - mockGitCalls( - JSON.stringify({ title: 'My field' }), - JSON.stringify({ title: 'My field', type: 'string' }), - ); + mockGitCalls(JSON.stringify({ title: 'My field' }), JSON.stringify({ title: 'My field', type: 'string' })); expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false); }); test('returns false when a functional field is removed', () => { - mockGitCalls( - JSON.stringify({ type: 'string', title: 'My field' }), - JSON.stringify({ title: 'My field' }), - ); + mockGitCalls(JSON.stringify({ type: 'string', title: 'My field' }), JSON.stringify({ title: 'My field' })); expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false); }); @@ -159,10 +148,7 @@ describe('isCosmeticOnlyJsonSchemaChange', () => { }); test('returns false when array content changes (not under a non-functional key)', () => { - mockGitCalls( - JSON.stringify({ enum: [1, 2, 3] }), - JSON.stringify({ enum: [1, 2, 4] }), - ); + mockGitCalls(JSON.stringify({ enum: [1, 2, 3] }), JSON.stringify({ enum: [1, 2, 4] })); expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false); }); @@ -176,8 +162,20 @@ describe('isCosmeticOnlyJsonSchemaChange', () => { test('returns true for a realistic actor.json with only title/description change', () => { mockGitCalls( - JSON.stringify({ actorSpecification: 1, name: 'my-actor', title: 'Old Title', description: 'Old description', version: '1.0' }), - JSON.stringify({ actorSpecification: 1, name: 'my-actor', title: 'New Title', description: 'New description', version: '1.0' }), + JSON.stringify({ + actorSpecification: 1, + name: 'my-actor', + title: 'Old Title', + description: 'Old description', + version: '1.0', + }), + JSON.stringify({ + actorSpecification: 1, + name: 'my-actor', + title: 'New Title', + description: 'New description', + version: '1.0', + }), ); expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true); }); diff --git a/test/unit/bin/git.test.ts b/test/unit/bin/git.test.ts index 420d701..a3c7ba9 100644 --- a/test/unit/bin/git.test.ts +++ b/test/unit/bin/git.test.ts @@ -1,4 +1,6 @@ -import { beforeEach, describe, expect, it, MockInstance, vi } from 'vitest'; +import type { MockInstance } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { getChangedFiles, getCommits } from '../../../bin/git.js'; import * as Utils from '../../../bin/utils.js'; diff --git a/test/unit/custom-matchers.test.ts b/test/unit/custom-matchers.test.ts index ed03b84..b1f7406 100644 --- a/test/unit/custom-matchers.test.ts +++ b/test/unit/custom-matchers.test.ts @@ -1,7 +1,8 @@ -import process from 'process'; -import { describe } from 'vitest'; +import process from 'node:process'; import { ApifyClient } from 'apify-client'; +import { describe } from 'vitest'; + import { testStandbyActor, testTestActor } from '../../lib/lib.js'; import { RunTestResult } from '../../lib/run-test-result.js'; @@ -11,13 +12,13 @@ enum PpeEventEnum { SEARCH_PAGE_SCRAPED = 'search-page-scraped', ADS_SCRAPED = 'ads-scraped', } -const PPE_EVENT_CONST = { - ACTOR_START: 'actor-start', - ITEM_PUSHED: 'search-page-scraped', - ADS_SCRAPED: 'ads-scraped', -} as const; +type PPE_EVENT_CONST = { + ACTOR_START: 'actor-start'; + ITEM_PUSHED: 'search-page-scraped'; + ADS_SCRAPED: 'ads-scraped'; +}; -// TODO: Remake these tests. k5MNKmaDGHlABDn2I run doesn't exist and we probably don't want to depend on fixed run +// TODO: Remake these tests. k5MNKmaDGHlABDn2I run doesn't exist and we probably don't want to depend on fixed run describe.skip('custom-matchers', { timeout: 100_000 }, () => { testTestActor('basic', async ({ expect }) => { const apifyClient = new ApifyClient({ token: process.env.TESTER_APIFY_TOKEN }); @@ -43,7 +44,7 @@ describe.skip('custom-matchers', { timeout: 100_000 }, () => { }, }); - await expect(runResult).toFinishWith<(typeof PPE_EVENT_CONST)[keyof typeof PPE_EVENT_CONST]>({ + await expect(runResult).toFinishWith({ datasetItemCount: { min: 1, max: 20 }, chargedEventCounts: { 'actor-start': 1, diff --git a/test/unit/should-built-and-test.test.ts b/test/unit/should-built-and-test.test.ts index 059be2b..3dfb131 100644 --- a/test/unit/should-built-and-test.test.ts +++ b/test/unit/should-built-and-test.test.ts @@ -40,7 +40,12 @@ describe('Should build and test parser', () => { test('Ignores dev-only readme', () => { const FILES = ['README.md', 'code/README.md', 'shared/README.md']; - const actorsChanged = getChangedActors({ actorConfigs: ACTOR_CONFIGS, isLatest: false, filepathsChanged: FILES, commits }); + const actorsChanged = getChangedActors({ + actorConfigs: ACTOR_CONFIGS, + isLatest: false, + filepathsChanged: FILES, + commits, + }); expect(actorsChanged).toEqual([]); }); @@ -48,7 +53,12 @@ describe('Should build and test parser', () => { test('Ignores other ignored files and folders', () => { const FILES = ['.vscode/', '.gitignore', '.husky/', '.eslintrc', '.editorconfig', '.actor/']; - const actorsChanged = getChangedActors({ actorConfigs: ACTOR_CONFIGS, isLatest: false, filepathsChanged: FILES, commits }); + const actorsChanged = getChangedActors({ + actorConfigs: ACTOR_CONFIGS, + isLatest: false, + filepathsChanged: FILES, + commits, + }); expect(actorsChanged).toEqual([]); }); @@ -56,7 +66,12 @@ describe('Should build and test parser', () => { test('Only builds latest for all Actors', () => { const FILES = ['shared/CHANGELOG.md', 'CHANGELOG.md']; - const actorsChanged = getChangedActors({ actorConfigs: ACTOR_CONFIGS, isLatest: true, filepathsChanged: FILES, commits }); + const actorsChanged = getChangedActors({ + actorConfigs: ACTOR_CONFIGS, + isLatest: true, + filepathsChanged: FILES, + commits, + }); expect(actorsChanged).toEqual(ACTOR_CONFIGS.filter(({ isStandalone }) => !isStandalone)); }); @@ -64,26 +79,42 @@ describe('Should build and test parser', () => { test('Code updated, tests miniactors', () => { const FILES = ['code/src/main.ts', 'package.json']; - const actorsChanged = getChangedActors({ actorConfigs: ACTOR_CONFIGS, isLatest: false, filepathsChanged: FILES, commits }); + const actorsChanged = getChangedActors({ + actorConfigs: ACTOR_CONFIGS, + isLatest: false, + filepathsChanged: FILES, + commits, + }); expect(actorsChanged).toEqual(ACTOR_CONFIGS.filter(({ isStandalone }) => !isStandalone)); }); test('Specific Actor functionality configs updated', () => { - const FILES = ['actors/lukaskrivka_testing-github-integration-1/.actor/actor.json', 'standalone-actors/lukaskrivka_test-standalone/Dockerfile']; + const FILES = [ + 'actors/lukaskrivka_testing-github-integration-1/.actor/actor.json', + 'standalone-actors/lukaskrivka_test-standalone/Dockerfile', + ]; // Default mock returns false = functional change - const actorsChanged = getChangedActors({ actorConfigs: ACTOR_CONFIGS, isLatest: false, filepathsChanged: FILES, commits }); + const actorsChanged = getChangedActors({ + actorConfigs: ACTOR_CONFIGS, + isLatest: false, + filepathsChanged: FILES, + commits, + }); expect(actorsChanged).toEqual([ACTOR_CONFIGS[0], ACTOR_CONFIGS[2]]); }); test('src/main.ts updated', () => { - const FILES = [ - 'src/main.ts', - ]; + const FILES = ['src/main.ts']; - const actorsChanged = getChangedActors({ actorConfigs: ACTOR_CONFIGS, isLatest: true, filepathsChanged: FILES, commits }); + const actorsChanged = getChangedActors({ + actorConfigs: ACTOR_CONFIGS, + isLatest: true, + filepathsChanged: FILES, + commits, + }); expect(actorsChanged).toEqual(ACTOR_CONFIGS.slice(0, 2)); }); @@ -96,15 +127,28 @@ describe('Should build and test parser', () => { ]; // Default mock returns false = functional change for the JSON file - const actorsChanged = getChangedActors({ actorConfigs: ACTOR_CONFIGS, isLatest: false, filepathsChanged: FILES, commits }); + const actorsChanged = getChangedActors({ + actorConfigs: ACTOR_CONFIGS, + isLatest: false, + filepathsChanged: FILES, + commits, + }); expect(actorsChanged).toEqual(ACTOR_CONFIGS); }); test('Specific Actor non-functional configs updated', () => { - const FILES = ['actors/lukaskrivka_testing-github-integration-2/.actor/README.md', 'standalone-actors/lukaskrivka_test-standalone/CHANGELOG.md']; + const FILES = [ + 'actors/lukaskrivka_testing-github-integration-2/.actor/README.md', + 'standalone-actors/lukaskrivka_test-standalone/CHANGELOG.md', + ]; - const actorsChanged = getChangedActors({ actorConfigs: ACTOR_CONFIGS, isLatest: false, filepathsChanged: FILES, commits }); + const actorsChanged = getChangedActors({ + actorConfigs: ACTOR_CONFIGS, + isLatest: false, + filepathsChanged: FILES, + commits, + }); expect(actorsChanged).toEqual([]); }); @@ -157,8 +201,8 @@ describe('Should build and test parser', () => { 'actors/lukaskrivka_testing-github-integration-2/.actor/input_schema.json', ]; // Actor 1 JSON is cosmetic-only, actor 2 JSON is functional - isCosmeticOnlyJsonSchemaSpy.mockImplementation((_commits, filepath: string) => - !filepath.includes('input_schema.json'), + isCosmeticOnlyJsonSchemaSpy.mockImplementation( + (_commits, filepath: string) => !filepath.includes('input_schema.json'), ); const actorsChanged = getChangedActors({ @@ -187,10 +231,20 @@ describe('Should build and test parser', () => { }); test('Google Maps real user-case that had undefined', () => { - const FILES = ['actors/compass_Google-Maps-Reviews-Scraper/.actor/INPUT_SCHEMA.json', 'actors/compass_crawler-google-places/.actor/INPUT_SCHEMA.json', - 'code/src/consts.ts', 'code/src/crawlers/cheerio/routes.ts', 'code/src/detail_page_handle.ts', 'code/src/enqueue_places.ts', - 'code/src/helper-classes/initialize-all.ts', 'code/src/helper-classes/stats.ts', 'code/src/helper-classes/unmatched-categories.ts', - 'code/src/main.ts', 'code/src/typedefs/general.ts', 'code/src/utils/background-enqueue.ts']; + const FILES = [ + 'actors/compass_Google-Maps-Reviews-Scraper/.actor/INPUT_SCHEMA.json', + 'actors/compass_crawler-google-places/.actor/INPUT_SCHEMA.json', + 'code/src/consts.ts', + 'code/src/crawlers/cheerio/routes.ts', + 'code/src/detail_page_handle.ts', + 'code/src/enqueue_places.ts', + 'code/src/helper-classes/initialize-all.ts', + 'code/src/helper-classes/stats.ts', + 'code/src/helper-classes/unmatched-categories.ts', + 'code/src/main.ts', + 'code/src/typedefs/general.ts', + 'code/src/utils/background-enqueue.ts', + ]; const ACTOR_CONFIGS_GOOGLE_MAPS: ActorConfig[] = [ { @@ -242,7 +296,10 @@ describe('Should build and test parser', () => { ]; const actorsChanged = getChangedActors({ - actorConfigs: ACTOR_CONFIGS_GOOGLE_MAPS, isLatest: false, filepathsChanged: FILES, commits, + actorConfigs: ACTOR_CONFIGS_GOOGLE_MAPS, + isLatest: false, + filepathsChanged: FILES, + commits, }); expect(actorsChanged).toEqual(ACTOR_CONFIGS_GOOGLE_MAPS.filter(({ isStandalone }) => !isStandalone));