mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
Every import, the catalog pin and the docs name @hanzo/app and github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and components/rooms.tsx is components/app.tsx.
99 lines
4.2 KiB
TypeScript
99 lines
4.2 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
||
import { checkoutUrl, payPage } from '@hanzo/app/lib/pay'
|
||
import { checkout } from '../../lib/checkout'
|
||
|
||
/**
|
||
* THE PAY ADDRESS, AND THE ORGANIZATION IT IS NAMED.
|
||
*
|
||
* pay bills and reads the ledger a request names, and it can only offer the
|
||
* organizations a token lists. Which of them this workspace is in is a fact
|
||
* this site holds in the browser, so the workspace's billing links carry it as
|
||
* `?org=`. The checkout links a marketing page draws carry none: they are
|
||
* written while the export prerenders, where there is no browser to ask.
|
||
*/
|
||
|
||
/** A bearer the way IAM signs one; only the middle segment is read. */
|
||
function bearer(claims: Record<string, unknown>): string {
|
||
const body = Buffer.from(JSON.stringify(claims)).toString('base64url')
|
||
return `header.${body}.signature`
|
||
}
|
||
|
||
/** A browser on hanzo.ai holding these entries, for the length of one call. */
|
||
function browser(entries: Record<string, string>): () => void {
|
||
const global = globalThis as { window?: unknown }
|
||
const had = 'window' in global
|
||
const was = global.window
|
||
const store = new Map(Object.entries(entries))
|
||
global.window = { location: { hostname: 'hanzo.ai' }, localStorage: { getItem: (k: string) => store.get(k) ?? null } }
|
||
return () => {
|
||
if (had) global.window = was
|
||
else delete global.window
|
||
}
|
||
}
|
||
|
||
const ACCESS = 'hanzo_iam_access_token'
|
||
const CHOSEN = 'hanzo_iam_current_org'
|
||
|
||
test('a billing page carries the one organization the token names', () => {
|
||
const done = browser({ [ACCESS]: bearer({ owner: 'hanzo', orgs: [{ org: 'acme', role: 'owner' }] }) })
|
||
try {
|
||
expect(payPage()).toBe('https://hanzo.ai/pay/?org=acme')
|
||
expect(payPage('/usage')).toBe('https://hanzo.ai/pay/usage?org=acme')
|
||
} finally {
|
||
done()
|
||
}
|
||
})
|
||
|
||
test('a billing page carries the organization chosen among several', () => {
|
||
const done = browser({
|
||
[ACCESS]: bearer({ owner: 'hanzo', orgs: [{ org: 'acme' }, { org: 'beta' }] }),
|
||
[CHOSEN]: 'beta',
|
||
})
|
||
try {
|
||
expect(payPage('/invoices')).toBe('https://hanzo.ai/pay/invoices?org=beta')
|
||
} finally {
|
||
done()
|
||
}
|
||
})
|
||
|
||
test('a billing page names no organization it cannot vouch for', () => {
|
||
const done = browser({ [ACCESS]: bearer({ owner: 'hanzo' }) })
|
||
try {
|
||
expect(payPage()).toBe('https://hanzo.ai/pay')
|
||
expect(payPage('/usage')).toBe('https://hanzo.ai/pay/usage')
|
||
} finally {
|
||
done()
|
||
}
|
||
})
|
||
|
||
// A plan opens on pay's cart, where a plan and its term are resolved. The root
|
||
// forwards a named plan there too, with its term; the cart is one hop fewer.
|
||
test('a checkout link opens the cart on the plan', () => {
|
||
expect(checkoutUrl()).toBe('https://hanzo.ai/pay')
|
||
expect(checkoutUrl('dev')).toBe('https://hanzo.ai/pay/cart?plan=dev')
|
||
expect(checkoutUrl('dev', 'https://hanzo.ai/home')).toBe(
|
||
'https://hanzo.ai/pay/cart?plan=dev&returnUrl=https%3A%2F%2Fhanzo.ai%2Fhome',
|
||
)
|
||
expect(checkoutUrl(undefined, 'https://hanzo.ai/home')).toBe(
|
||
'https://hanzo.ai/pay/?returnUrl=https%3A%2F%2Fhanzo.ai%2Fhome',
|
||
)
|
||
})
|
||
|
||
// A plan is bought on pay's card step (lib/checkout): the plan, a year where one
|
||
// is asked for, the seats past one, the coupon, the organization billed, and the
|
||
// address pay returns to. A month and one seat are pay's defaults.
|
||
test('a plan is bought on pay’s card step, carrying its term, seats, coupon and organization', () => {
|
||
const back = 'https://hanzo.ai/chat'
|
||
expect(checkout({ plan: 'dev', returnUrl: back })).toBe('/pay/confirm/card?plan=dev&returnUrl=https%3A%2F%2Fhanzo.ai%2Fchat')
|
||
expect(checkout({ plan: 'dev', interval: 'month', quantity: 1, returnUrl: back })).toBe(checkout({ plan: 'dev', returnUrl: back }))
|
||
expect(checkout({ plan: 'max-5x', interval: 'year', returnUrl: back })).toBe(
|
||
'/pay/confirm/card?plan=max-5x&interval=year&returnUrl=https%3A%2F%2Fhanzo.ai%2Fchat',
|
||
)
|
||
expect(checkout({ plan: 'dev', interval: 'month', coupon: '50OFF', returnUrl: back })).toBe(
|
||
'/pay/confirm/card?plan=dev&coupon=50OFF&returnUrl=https%3A%2F%2Fhanzo.ai%2Fchat',
|
||
)
|
||
expect(checkout({ plan: 'team', interval: 'month', quantity: 3, org: 'acme', returnUrl: back })).toBe(
|
||
'/pay/confirm/card?plan=team&quantity=3&org=acme&returnUrl=https%3A%2F%2Fhanzo.ai%2Fchat',
|
||
)
|
||
})
|