chore: kanban template

This commit is contained in:
e2e
2026-06-21 23:23:22 +02:00
commit 254c260123
209 changed files with 215578 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
import { Given, When, Then } from '@cucumber/cucumber'
import { expect } from '@playwright/test'
import type { AppWorld } from '../support/world.js'
/**
* Generic step library — shared across every project that copies this harness.
*
* Project-specific business steps (e.g. "I create a booking for <date>")
* belong in their own *.steps.ts files. Keep this file framework-agnostic.
*/
Given('I visit {string}', async function (this: AppWorld, path: string) {
await this.gotoApp(path)
})
Given(
'I am logged in as {string} with password {string}',
async function (this: AppWorld, email: string, password: string) {
await this.login(email, password)
},
)
When('I log in as {string} with password {string}', async function (
this: AppWorld,
email: string,
password: string,
) {
await this.login(email, password)
})
When('I log out', async function (this: AppWorld) {
await this.logout()
})
When(
'I fill {string} with {string}',
async function (this: AppWorld, selector: string, value: string) {
await this.page.locator(selector).fill(value)
},
)
When('I click {string}', async function (this: AppWorld, label: string) {
await this.page.getByRole('button', { name: label }).first().click()
})
Then('I see {string}', async function (this: AppWorld, text: string) {
await this.expectText(text)
})
Then('I do not see {string}', async function (this: AppWorld, text: string) {
await expect(
this.page.getByText(text, { exact: false }).first(),
).toBeHidden()
})
Then('the URL contains {string}', async function (this: AppWorld, fragment: string) {
await this.page.waitForURL((url) => url.toString().includes(fragment))
})

View File

@@ -0,0 +1,106 @@
import { Given, When, Then } from '@cucumber/cucumber'
import { expect } from '@playwright/test'
import type { AppWorld } from '../support/world.js'
import { config } from '../support/types.js'
interface ContentWorld extends AppWorld {
cardId: string
uploadUrl: string
signedUrl: string
uploadStatus: number
}
Given('a card exists', async function (this: ContentWorld) {
const res = await fetch(`${config.apiUrl}/cards`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ title: 'Attachment test card' }),
})
expect(res.ok, `POST /cards failed: ${res.status}`).toBe(true)
const body = (await res.json()) as { cardId: string }
this.cardId = body.cardId
})
When(
'I request an upload URL for filename {string} and content type {string}',
async function (this: ContentWorld, fileName: string, contentType: string) {
const res = await fetch(
`${config.apiUrl}/cards/${this.cardId}/attachments/upload-url`,
{
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ cardId: this.cardId, fileName, contentType }),
},
)
expect(res.ok, `upload-url request failed: ${res.status} ${await res.text()}`).toBe(true)
const body = (await res.json()) as { uploadUrl: string }
this.uploadUrl = body.uploadUrl
},
)
When(
'I PUT {string} to the upload URL',
async function (this: ContentWorld, content: string) {
const res = await fetch(this.uploadUrl, {
method: 'PUT',
body: content,
headers: { 'content-type': 'text/plain' },
})
this.uploadStatus = res.status
},
)
Then(
'the upload response status is 200 or 201',
function (this: ContentWorld) {
expect(
this.uploadStatus === 200 || this.uploadStatus === 201,
`expected 200 or 201 from upload PUT, got ${this.uploadStatus}`,
).toBe(true)
},
)
When(
'I request a signed download URL for filename {string}',
async function (this: ContentWorld, fileName: string) {
const res = await fetch(
`${config.apiUrl}/cards/${this.cardId}/attachments/${encodeURIComponent(fileName)}/signed-url`,
)
expect(res.ok, `signed-url request failed: ${res.status} ${await res.text()}`).toBe(true)
const body = (await res.json()) as { signedUrl: string }
this.signedUrl = body.signedUrl
},
)
Then(
'the signed URL response contains a signedUrl',
function (this: ContentWorld) {
expect(typeof this.signedUrl).toBe('string')
expect(this.signedUrl.length).toBeGreaterThan(0)
},
)
Then(
'downloading the signed URL returns {string}',
async function (this: ContentWorld, expectedContent: string) {
const res = await fetch(this.signedUrl)
expect(res.ok, `signed download failed: ${res.status}`).toBe(true)
const text = await res.text()
expect(text).toBe(expectedContent)
},
)
When('I strip the signature from the signed URL', function (this: ContentWorld) {
const url = new URL(this.signedUrl)
url.searchParams.delete('sig')
this.signedUrl = url.toString()
})
Then(
'fetching the unsigned URL returns a 4xx status',
async function (this: ContentWorld) {
const res = await fetch(this.signedUrl)
expect(res.status, `expected 4xx for unsigned access, got ${res.status}`).toBeGreaterThanOrEqual(400)
expect(res.status).toBeLessThan(500)
},
)