Skip to content

Commit beb9926

Browse files
committed
✅ Skip e2e tests on Bitbucket Server
1 parent eaed3b2 commit beb9926

8 files changed

+93
-110
lines changed

.env

+3
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ BITBUCKET_SERVER_URL=
66
BITBUCKET_SERVER_TOKEN=
77
BITBUCKET_SERVER_TEST_PROJECT_KEY=
88
BITBUCKET_SERVER_TEST_PROJECT_NAME=
9+
10+
SKIP_BITBUCKET_CLOUD=false
11+
SKIP_BITBUCKET_SERVER=true

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
},
5555
"devDependencies": {
5656
"@eslint/js": "^9.21.0",
57+
"@natoboram/load_env": "^1.0.0",
5758
"@types/node": "^22.13.5",
5859
"dotenv": "^16.4.7",
5960
"eslint": "^9.21.0",

pnpm-lock.yaml

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/cloud/repositories.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { test } from "vitest"
2+
import { SKIP_BITBUCKET_CLOUD } from "../env.ts"
23
import { client } from "./client.ts"
34

4-
test("GET /repositories", async ({ expect }) => {
5+
test.skipIf(SKIP_BITBUCKET_CLOUD)("GET /repositories", async ({ expect }) => {
56
const got = await client.GET("/repositories")
67

78
expect(got.data?.next).toBeTypeOf("string")

tests/env.ts

+22-64
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,21 @@
1-
import { config } from "dotenv"
2-
import path from "path"
1+
import { envBool, envString, envUrl, loadEnv } from "@natoboram/load_env"
32

43
/**
54
* @see https://nodejs.org/en/learn/getting-started/nodejs-the-difference-between-development-and-production
65
* @see https://vitest.dev/guide/migration.html#envs
76
*/
8-
export type NodeEnv = (typeof nodeEnvs)[keyof typeof nodeEnvs]
9-
export type ProcessEnv = typeof process.env
7+
type NodeEnv = (typeof NodeEnv)[keyof typeof NodeEnv]
108

11-
interface LoadedEnv extends ProcessEnv {
12-
readonly NODE_ENV: NodeEnv
9+
function isNodeEnv(value: unknown): value is NodeEnv {
10+
return Object.values<unknown>(NodeEnv).includes(value)
1311
}
1412

15-
function envString(key: string) {
16-
const value = parsed[key]
17-
if (!value) throw new Error(`$${key} is missing`)
18-
return value
19-
}
20-
21-
function envUrl(key: string) {
22-
const str = envString(key)
23-
try {
24-
return new URL(str)
25-
} catch (error) {
26-
throw new Error(`$${key} is not a URL: ${str}`, { cause: error })
27-
}
28-
}
29-
30-
export function isNodeEnv(value: unknown): value is NodeEnv {
31-
return Object.values<unknown>(nodeEnvs).includes(value)
32-
}
33-
34-
/** Loads environment variables from the `.env` files. `NODE_ENV` has to be
35-
* set in the environment and will not be picked up from there.
36-
*
37-
* If `NODE_ENV` is not set, it will default to `development`.
38-
*
39-
* Environment variables are loaded in the following order:
40-
*
41-
* 1. `.env.development.local`
42-
* 2. `.env.development`
43-
* 3. `.env.local`
44-
* 4. `.env`
45-
*/
46-
function loadEnv(): LoadedEnv {
47-
const cwd = process.cwd()
48-
const NODE_ENV = toNodeEnv(process.env.NODE_ENV?.trim())
49-
50-
const { parsed, error } = config({
51-
path: [
52-
path.resolve(cwd, `.env.${NODE_ENV}.local`),
53-
path.resolve(cwd, `.env.${NODE_ENV}`),
54-
path.resolve(cwd, ".env.local"),
55-
path.resolve(cwd, ".env"),
56-
],
57-
})
58-
59-
if (!parsed)
60-
throw new Error("Environment variables could not be loaded.", {
61-
cause: error,
62-
})
63-
64-
const merged = Object.assign(parsed, process.env, { NODE_ENV })
65-
process.env = merged
66-
return merged
67-
}
68-
69-
export function toNodeEnv(value: unknown): NodeEnv {
13+
function toNodeEnv(value: unknown): NodeEnv {
7014
if (isNodeEnv(value)) return value
71-
return nodeEnvs.development
15+
return NodeEnv.development
7216
}
7317

74-
const nodeEnvs = {
18+
const NodeEnv = {
7519
development: "development",
7620
production: "production",
7721
/**
@@ -80,18 +24,32 @@ const nodeEnvs = {
8024
*/
8125
test: "test",
8226
} as const
27+
8328
const parsed = loadEnv()
29+
30+
export const NODE_ENV = toNodeEnv(parsed.NODE_ENV)
31+
8432
export const BITBUCKET_CLOUD_URL = envUrl("BITBUCKET_CLOUD_URL")
8533
export const BITBUCKET_CLOUD_USERNAME = envString("BITBUCKET_CLOUD_USERNAME")
8634
export const BITBUCKET_CLOUD_APP_PASSWORD = envString(
8735
"BITBUCKET_CLOUD_APP_PASSWORD",
8836
)
37+
8938
export const BITBUCKET_SERVER_URL = envUrl("BITBUCKET_SERVER_URL")
9039
export const BITBUCKET_SERVER_TOKEN = envString("BITBUCKET_SERVER_TOKEN")
91-
export const NODE_ENV = parsed.NODE_ENV
9240
export const BITBUCKET_SERVER_TEST_PROJECT_KEY = envString(
9341
"BITBUCKET_SERVER_TEST_PROJECT_KEY",
9442
)
9543
export const BITBUCKET_SERVER_TEST_PROJECT_NAME = envString(
9644
"BITBUCKET_SERVER_TEST_PROJECT_NAME",
9745
)
46+
47+
export const SKIP_BITBUCKET_CLOUD = envBool("SKIP_BITBUCKET_CLOUD")
48+
49+
/** Considering that single instance for a single user costs 2300 USD annually,
50+
* most people aren't going to have a Bitbucket Data Center instance to test on.
51+
* Therefore, end-to-end tests for Bitbucket Data Center are skipped by default.
52+
*
53+
* @see https://www.atlassian.com/software/bitbucket/enterprise
54+
*/
55+
export const SKIP_BITBUCKET_SERVER = envBool("SKIP_BITBUCKET_SERVER")

tests/server/projects.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { describe, test } from "vitest"
22
import {
33
BITBUCKET_SERVER_TEST_PROJECT_KEY,
44
BITBUCKET_SERVER_TEST_PROJECT_NAME,
5+
SKIP_BITBUCKET_SERVER,
56
} from "../env.ts"
67
import { client } from "./client.ts"
78

8-
describe("Projects", () => {
9+
describe.skipIf(SKIP_BITBUCKET_SERVER)("Projects", () => {
910
const key = BITBUCKET_SERVER_TEST_PROJECT_KEY
1011
const name = BITBUCKET_SERVER_TEST_PROJECT_NAME
1112

tests/server/repositories.test.ts

+52-44
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,69 @@ import { describe, test } from "vitest"
22
import {
33
BITBUCKET_SERVER_TEST_PROJECT_KEY,
44
BITBUCKET_SERVER_TEST_PROJECT_NAME,
5+
SKIP_BITBUCKET_SERVER,
56
} from "../env.ts"
67
import { client } from "./client.ts"
78

8-
describe("Repositories", { concurrent: false, sequential: true }, () => {
9-
const projectKey = BITBUCKET_SERVER_TEST_PROJECT_KEY
10-
const projectName = BITBUCKET_SERVER_TEST_PROJECT_NAME
11-
const slug = "test-repository"
12-
const name = "Test Repository"
9+
describe.skipIf(SKIP_BITBUCKET_SERVER)(
10+
"Repositories",
11+
{ concurrent: false, sequential: true },
12+
() => {
13+
const projectKey = BITBUCKET_SERVER_TEST_PROJECT_KEY
14+
const projectName = BITBUCKET_SERVER_TEST_PROJECT_NAME
15+
const slug = "test-repository"
16+
const name = "Test Repository"
1317

14-
test("Create repository", async ({ expect }) => {
15-
const created = await client.POST(
16-
"/api/latest/projects/{projectKey}/repos",
17-
{ params: { path: { projectKey } }, body: { name, scmId: "git", slug } },
18-
)
18+
test("Create repository", async ({ expect }) => {
19+
const created = await client.POST(
20+
"/api/latest/projects/{projectKey}/repos",
21+
{
22+
params: { path: { projectKey } },
23+
body: { name, scmId: "git", slug },
24+
},
25+
)
1926

20-
if (created.error)
21-
console.error("Failed to create a repository", created.error)
27+
if (created.error)
28+
console.error("Failed to create a repository", created.error)
2229

23-
expect(created).toMatchObject({
24-
data: {
30+
expect(created).toMatchObject({
31+
data: {
32+
slug,
33+
name,
34+
project: { key: projectKey, name: projectName },
35+
scmId: "git",
36+
},
37+
response: { status: 201 },
38+
})
39+
})
40+
41+
test("Get a repository", async ({ expect }) => {
42+
const repository = await client.GET(
43+
"/api/latest/projects/{projectKey}/repos/{repositorySlug}",
44+
{ params: { path: { projectKey, repositorySlug: slug } } },
45+
)
46+
47+
if (repository.error)
48+
console.error("Failed to get a repository", repository.error)
49+
50+
expect(repository.data).toMatchObject({
2551
slug,
2652
name,
2753
project: { key: projectKey, name: projectName },
2854
scmId: "git",
29-
},
30-
response: { status: 201 },
31-
})
32-
})
33-
34-
test("Get a repository", async ({ expect }) => {
35-
const repository = await client.GET(
36-
"/api/latest/projects/{projectKey}/repos/{repositorySlug}",
37-
{ params: { path: { projectKey, repositorySlug: slug } } },
38-
)
39-
40-
if (repository.error)
41-
console.error("Failed to get a repository", repository.error)
42-
43-
expect(repository.data).toMatchObject({
44-
slug,
45-
name,
46-
project: { key: projectKey, name: projectName },
47-
scmId: "git",
55+
})
4856
})
49-
})
5057

51-
test("Delete a repository", async ({ expect }) => {
52-
const deleted = await client.DELETE(
53-
"/api/latest/projects/{projectKey}/repos/{repositorySlug}",
54-
{ params: { path: { projectKey, repositorySlug: slug } } },
55-
)
58+
test("Delete a repository", async ({ expect }) => {
59+
const deleted = await client.DELETE(
60+
"/api/latest/projects/{projectKey}/repos/{repositorySlug}",
61+
{ params: { path: { projectKey, repositorySlug: slug } } },
62+
)
5663

57-
if (deleted.error)
58-
console.error("Failed to delete a repository", deleted.error)
64+
if (deleted.error)
65+
console.error("Failed to delete a repository", deleted.error)
5966

60-
expect(deleted.response.status).toBe(202)
61-
})
62-
})
67+
expect(deleted.response.status).toBe(202)
68+
})
69+
},
70+
)

tsconfig.eslint.json

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"isolatedModules": true,
3232
"verbatimModuleSyntax": true,
3333
"isolatedDeclarations": true,
34+
"erasableSyntaxOnly": true,
3435
"forceConsistentCasingInFileNames": true,
3536

3637
/* Type Checking */

0 commit comments

Comments
 (0)