@jskit-ai/payments-web 0.1.61 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +11 -4
- package/locales/en.json +55 -0
- package/package.json +4 -2
- package/src/client/components/PaymentAccount.vue +59 -36
- package/src/client/i18n.js +6 -0
- package/test/i18nCatalog.test.js +16 -0
- package/test/paymentAccount.test.js +3 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 - 2026-10-29
|
|
4
|
+
|
|
5
|
+
- Translation ready: every user-visible text is a message key with its English in `locales/en.json`, so an app translates it by supplying translation files only. With no translations, texts and behaviour are unchanged. See the guide "Translating a JSKIT app".
|
|
6
|
+
|
|
7
|
+
### New message keys (43)
|
|
8
|
+
|
|
9
|
+
All keys in `locales/en.json` (exported as `./locales/en.json`) are new in this version.
|
|
10
|
+
|
|
11
|
+
<details><summary>Key list</summary>
|
|
12
|
+
|
|
13
|
+
- `jskit.payments.account.region`
|
|
14
|
+
- `jskit.payments.account.retryDetails`
|
|
15
|
+
- `jskit.payments.account.availableCredits`
|
|
16
|
+
- `jskit.payments.account.includedFeatures`
|
|
17
|
+
- `jskit.payments.account.noPaidFeatures`
|
|
18
|
+
- `jskit.payments.account.subscriptions`
|
|
19
|
+
- `jskit.payments.account.periodEnd`
|
|
20
|
+
- `jskit.payments.account.noSubscription`
|
|
21
|
+
- `jskit.payments.account.manageBilling`
|
|
22
|
+
- `jskit.payments.account.refreshDetails`
|
|
23
|
+
- `jskit.payments.account.adminManages`
|
|
24
|
+
- `jskit.payments.account.useManageBilling`
|
|
25
|
+
- `jskit.payments.account.confirmationNote`
|
|
26
|
+
- `jskit.payments.account.availablePlans`
|
|
27
|
+
- `jskit.payments.account.renewalCredits`
|
|
28
|
+
- `jskit.payments.account.choosePlan`
|
|
29
|
+
- `jskit.payments.account.choosePlanNamed`
|
|
30
|
+
- `jskit.payments.account.notAvailableForCheckout`
|
|
31
|
+
- `jskit.payments.account.noPlans`
|
|
32
|
+
- `jskit.payments.account.unavailable`
|
|
33
|
+
- `jskit.payments.account.status.active`
|
|
34
|
+
- `jskit.payments.account.status.trialing`
|
|
35
|
+
- `jskit.payments.account.status.pastDue`
|
|
36
|
+
- `jskit.payments.account.status.paused`
|
|
37
|
+
- `jskit.payments.account.status.canceled`
|
|
38
|
+
- `jskit.payments.account.status.unpaid`
|
|
39
|
+
- `jskit.payments.account.status.incomplete`
|
|
40
|
+
- `jskit.payments.account.status.incompleteExpired`
|
|
41
|
+
- `jskit.payments.account.history.region`
|
|
42
|
+
- `jskit.payments.account.history.viewTransactions`
|
|
43
|
+
- `jskit.payments.account.history.viewSubscriptions`
|
|
44
|
+
- `jskit.payments.account.history.retry`
|
|
45
|
+
- `jskit.payments.account.history.unpaidNote`
|
|
46
|
+
- `jskit.payments.account.history.records`
|
|
47
|
+
- `jskit.payments.account.history.invoice`
|
|
48
|
+
- `jskit.payments.account.history.transaction`
|
|
49
|
+
- `jskit.payments.account.history.subscription`
|
|
50
|
+
- `jskit.payments.account.history.statusAndDate`
|
|
51
|
+
- `jskit.payments.account.history.total`
|
|
52
|
+
- `jskit.payments.account.history.totalPending`
|
|
53
|
+
- `jskit.payments.account.history.totalAndPaid`
|
|
54
|
+
- `jskit.payments.account.history.empty`
|
|
55
|
+
- `jskit.payments.account.history.nextPage`
|
|
56
|
+
|
|
57
|
+
</details>
|
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
`@jskit-ai/payments-web` supplies a Vue/Vuetify account component for generated or
|
|
4
4
|
hand-written JavaScript applications. It has no Vibe64, Online, Genesis, provider
|
|
5
|
-
SDK, credential, file-discovery or HTTP dependency
|
|
5
|
+
SDK, credential, file-discovery or HTTP dependency; its only JSKIT peer is
|
|
6
|
+
`@jskit-ai/kernel`, for the client text helper. The application mounts it in
|
|
6
7
|
its existing billing page or semantic placement. It does not register routes or
|
|
7
8
|
change shell navigation automatically. This worktree package is not yet published.
|
|
8
9
|
|
|
@@ -23,7 +24,12 @@ Import `PaymentAccount` from
|
|
|
23
24
|
| `history` | One authorized `{collection, items, nextCursor}` provider page, or null |
|
|
24
25
|
| `historyLoading` | History request pending; displays skeletons and disables page/collection requests |
|
|
25
26
|
| `historyError` | Safe history read failure; hides stale rows and offers Retry |
|
|
26
|
-
| `locale` | Optional locale for integer credit and UTC date formatting |
|
|
27
|
+
| `locale` | Optional locale for integer credit and UTC date formatting; defaults to the app's active language |
|
|
28
|
+
|
|
29
|
+
Every text the component renders is a `jskit.payments.account.*` message in
|
|
30
|
+
`locales/en.json`. With the app's vue-i18n registered through JSKIT, it shows the
|
|
31
|
+
user's language; without it, the English is shown as before. Plan names, price
|
|
32
|
+
labels, feature names and the `loadError`/`historyError` texts are the app's own.
|
|
27
33
|
|
|
28
34
|
Subscription records contain `id`, `planId`, `status`, and `periodEnd` in UTC
|
|
29
35
|
milliseconds. `payments-core`'s authorized `checkout.account({actor, subjectId})`
|
|
@@ -126,8 +132,9 @@ insert a second error banner or duplicate that request state. Re-read on the
|
|
|
126
132
|
return route; a success query parameter must never grant paid access.
|
|
127
133
|
|
|
128
134
|
Laravel owns its native server and UI implementation. An Inertia/Vue application
|
|
129
|
-
can choose this presentational component
|
|
130
|
-
|
|
135
|
+
can choose this presentational component (installing its `@jskit-ai/kernel` peer
|
|
136
|
+
for the text helper), but no JSKIT runtime is required for Laravel to implement
|
|
137
|
+
the documented JSON account/plan display shape and actions.
|
|
131
138
|
|
|
132
139
|
## Evidence
|
|
133
140
|
|
package/locales/en.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"jskit": {
|
|
3
|
+
"payments": {
|
|
4
|
+
"account": {
|
|
5
|
+
"region": "Subscription and billing",
|
|
6
|
+
"retryDetails": "Retry billing details",
|
|
7
|
+
"availableCredits": "Available credits",
|
|
8
|
+
"includedFeatures": "Included features",
|
|
9
|
+
"noPaidFeatures": "No paid features active",
|
|
10
|
+
"subscriptions": "Subscriptions",
|
|
11
|
+
"periodEnd": "Period end: {date} (UTC)",
|
|
12
|
+
"noSubscription": "No subscription yet.",
|
|
13
|
+
"manageBilling": "Manage billing",
|
|
14
|
+
"refreshDetails": "Refresh billing details",
|
|
15
|
+
"adminManages": "A billing administrator can manage this account.",
|
|
16
|
+
"useManageBilling": "Use Manage billing to update your subscription or payment details.",
|
|
17
|
+
"confirmationNote": "Access and credits update after payment confirmation. Returning from checkout alone does not activate a plan.",
|
|
18
|
+
"availablePlans": "Available plans",
|
|
19
|
+
"renewalCredits": "{credits} credits per paid renewal; unused renewal credits expire at period end.",
|
|
20
|
+
"choosePlan": "Choose plan",
|
|
21
|
+
"choosePlanNamed": "Choose {plan}",
|
|
22
|
+
"notAvailableForCheckout": "Not available for checkout yet.",
|
|
23
|
+
"noPlans": "No plans are available yet.",
|
|
24
|
+
"unavailable": "Billing details are not available yet.",
|
|
25
|
+
"status": {
|
|
26
|
+
"active": "Active",
|
|
27
|
+
"trialing": "Trial",
|
|
28
|
+
"pastDue": "Payment overdue",
|
|
29
|
+
"paused": "Paused",
|
|
30
|
+
"canceled": "Canceled",
|
|
31
|
+
"unpaid": "Unpaid",
|
|
32
|
+
"incomplete": "Payment incomplete",
|
|
33
|
+
"incompleteExpired": "Payment expired"
|
|
34
|
+
},
|
|
35
|
+
"history": {
|
|
36
|
+
"region": "Billing history",
|
|
37
|
+
"viewTransactions": "View invoices and transactions",
|
|
38
|
+
"viewSubscriptions": "View subscription history",
|
|
39
|
+
"retry": "Retry billing history",
|
|
40
|
+
"unpaidNote": "Invoices and transactions include unpaid records. Their totals do not confirm payment.",
|
|
41
|
+
"records": "Provider billing records",
|
|
42
|
+
"invoice": "Invoice {id}",
|
|
43
|
+
"transaction": "Transaction {id}",
|
|
44
|
+
"subscription": "Subscription {id}",
|
|
45
|
+
"statusAndDate": "{status} · {date} (UTC)",
|
|
46
|
+
"total": "Total: {total}",
|
|
47
|
+
"totalPending": "Not available yet",
|
|
48
|
+
"totalAndPaid": "Total: {total} · Paid: {paid}",
|
|
49
|
+
"empty": "No billing records found.",
|
|
50
|
+
"nextPage": "Next billing page"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/payments-web",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Application-owned billing views for JSKIT Vue applications.",
|
|
6
6
|
"exports": {
|
|
7
|
-
"./client/components/PaymentAccount": "./src/client/components/PaymentAccount.vue"
|
|
7
|
+
"./client/components/PaymentAccount": "./src/client/components/PaymentAccount.vue",
|
|
8
|
+
"./locales/en.json": "./locales/en.json"
|
|
8
9
|
},
|
|
9
10
|
"peerDependencies": {
|
|
11
|
+
"@jskit-ai/kernel": "0.2.1",
|
|
10
12
|
"vue": "^3.5.13",
|
|
11
13
|
"vuetify": "^4.0.0"
|
|
12
14
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
+
import { useJskitText } from "../i18n.js";
|
|
3
4
|
|
|
4
5
|
const props = defineProps({
|
|
5
6
|
plans: { type: Array, default: () => [] },
|
|
@@ -19,13 +20,35 @@ const subscriptions = computed(() => props.account?.subscriptions ?? []);
|
|
|
19
20
|
const hasSubscription = computed(() => subscriptions.value.some((item) =>
|
|
20
21
|
!["canceled", "incomplete_expired"].includes(item.status)));
|
|
21
22
|
const disabled = computed(() => props.pending || props.loading || Boolean(props.loadError) || !props.account || !props.canManage);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const jskitText = useJskitText();
|
|
24
|
+
const { t } = jskitText;
|
|
25
|
+
const statusKeys = {
|
|
26
|
+
active: "jskit.payments.account.status.active",
|
|
27
|
+
trialing: "jskit.payments.account.status.trialing",
|
|
28
|
+
past_due: "jskit.payments.account.status.pastDue",
|
|
29
|
+
paused: "jskit.payments.account.status.paused",
|
|
30
|
+
canceled: "jskit.payments.account.status.canceled",
|
|
31
|
+
unpaid: "jskit.payments.account.status.unpaid",
|
|
32
|
+
incomplete: "jskit.payments.account.status.incomplete",
|
|
33
|
+
incomplete_expired: "jskit.payments.account.status.incompleteExpired"
|
|
26
34
|
};
|
|
27
|
-
const
|
|
28
|
-
|
|
35
|
+
const historyKindKeys = {
|
|
36
|
+
invoice: "jskit.payments.account.history.invoice",
|
|
37
|
+
transaction: "jskit.payments.account.history.transaction"
|
|
38
|
+
};
|
|
39
|
+
const historyTitle = (item) =>
|
|
40
|
+
t(Object.hasOwn(historyKindKeys, item.kind) ? historyKindKeys[item.kind] : "jskit.payments.account.history.subscription", { id: item.id });
|
|
41
|
+
const historyTotal = (item) => {
|
|
42
|
+
const total = item.totalLabel ?? t("jskit.payments.account.history.totalPending");
|
|
43
|
+
return item.paidLabel
|
|
44
|
+
? t("jskit.payments.account.history.totalAndPaid", { total, paid: item.paidLabel })
|
|
45
|
+
: t("jskit.payments.account.history.total", { total });
|
|
46
|
+
};
|
|
47
|
+
const statusLabel = (status) => Object.hasOwn(statusKeys, status) ? t(statusKeys[status]) : status;
|
|
48
|
+
// An explicit locale prop wins; otherwise numbers and dates follow the app's language.
|
|
49
|
+
const locale = () => props.locale ?? jskitText.locale;
|
|
50
|
+
const number = (value) => new Intl.NumberFormat(locale()).format(value);
|
|
51
|
+
const date = (value) => new Intl.DateTimeFormat(locale(), { dateStyle: "medium", timeZone: "UTC" }).format(value);
|
|
29
52
|
const planName = (id) => props.plans.find((plan) => plan.id === id)?.name ?? id;
|
|
30
53
|
function checkout(plan) {
|
|
31
54
|
if (!disabled.value && !hasSubscription.value && plan.available === true) emit("checkout", plan.id);
|
|
@@ -36,68 +59,68 @@ function loadHistory(collection, after = null) {
|
|
|
36
59
|
</script>
|
|
37
60
|
|
|
38
61
|
<template>
|
|
39
|
-
<section class="payment-account" aria-label="
|
|
62
|
+
<section class="payment-account" :aria-label="t('jskit.payments.account.region')" :aria-busy="loading || pending">
|
|
40
63
|
<v-skeleton-loader v-if="loading && !account" type="article, list-item-two-line, actions" />
|
|
41
64
|
<div v-else-if="loadError" role="alert">
|
|
42
65
|
<p>{{ loadError }}</p>
|
|
43
|
-
<v-btn variant="tonal" :disabled="loading" @click="emit('refresh')">
|
|
66
|
+
<v-btn variant="tonal" :disabled="loading" @click="emit('refresh')">{{ t('jskit.payments.account.retryDetails') }}</v-btn>
|
|
44
67
|
</div>
|
|
45
68
|
<template v-else-if="account">
|
|
46
69
|
<dl class="payment-account__summary">
|
|
47
|
-
<div><dt>
|
|
48
|
-
<div><dt>
|
|
70
|
+
<div><dt>{{ t('jskit.payments.account.availableCredits') }}</dt><dd>{{ number(account.balance) }}</dd></div>
|
|
71
|
+
<div><dt>{{ t('jskit.payments.account.includedFeatures') }}</dt><dd>{{ account.features.length ? account.features.join(', ') : t('jskit.payments.account.noPaidFeatures') }}</dd></div>
|
|
49
72
|
</dl>
|
|
50
|
-
<ul v-if="subscriptions.length" class="payment-account__subscriptions" aria-label="
|
|
73
|
+
<ul v-if="subscriptions.length" class="payment-account__subscriptions" :aria-label="t('jskit.payments.account.subscriptions')">
|
|
51
74
|
<li v-for="subscription in subscriptions" :key="subscription.id">
|
|
52
75
|
<strong>{{ planName(subscription.planId) }}</strong>
|
|
53
|
-
<span>{{
|
|
54
|
-
<span v-if="subscription.periodEnd > 0">
|
|
76
|
+
<span>{{ statusLabel(subscription.status) }}</span>
|
|
77
|
+
<span v-if="subscription.periodEnd > 0">{{ t('jskit.payments.account.periodEnd', { date: date(subscription.periodEnd) }) }}</span>
|
|
55
78
|
</li>
|
|
56
79
|
</ul>
|
|
57
|
-
<p v-else>
|
|
80
|
+
<p v-else>{{ t('jskit.payments.account.noSubscription') }}</p>
|
|
58
81
|
<div class="payment-account__actions">
|
|
59
|
-
<v-btn v-if="account.hasCustomer" variant="tonal" :disabled="disabled" @click="emit('portal')">
|
|
60
|
-
<v-btn variant="text" :disabled="loading || pending" @click="emit('refresh')">
|
|
82
|
+
<v-btn v-if="account.hasCustomer" variant="tonal" :disabled="disabled" @click="emit('portal')">{{ t('jskit.payments.account.manageBilling') }}</v-btn>
|
|
83
|
+
<v-btn variant="text" :disabled="loading || pending" @click="emit('refresh')">{{ t('jskit.payments.account.refreshDetails') }}</v-btn>
|
|
61
84
|
</div>
|
|
62
|
-
<p v-if="!canManage">
|
|
63
|
-
<p v-else-if="hasSubscription">
|
|
64
|
-
<p>
|
|
65
|
-
<ul v-if="plans.length" class="payment-account__plans" aria-label="
|
|
85
|
+
<p v-if="!canManage">{{ t('jskit.payments.account.adminManages') }}</p>
|
|
86
|
+
<p v-else-if="hasSubscription">{{ t('jskit.payments.account.useManageBilling') }}</p>
|
|
87
|
+
<p>{{ t('jskit.payments.account.confirmationNote') }}</p>
|
|
88
|
+
<ul v-if="plans.length" class="payment-account__plans" :aria-label="t('jskit.payments.account.availablePlans')">
|
|
66
89
|
<li v-for="plan in plans" :key="plan.id" class="payment-account__plan">
|
|
67
90
|
<strong>{{ plan.name }}</strong>
|
|
68
91
|
<p>{{ plan.priceLabel }}</p>
|
|
69
92
|
<ul v-if="plan.features.length"><li v-for="feature in plan.features" :key="feature">{{ feature }}</li></ul>
|
|
70
|
-
<p>{{
|
|
71
|
-
<v-btn variant="flat" color="primary" :disabled="disabled || hasSubscription || plan.available !== true" :aria-label="
|
|
72
|
-
<p v-if="plan.available !== true">
|
|
93
|
+
<p>{{ t('jskit.payments.account.renewalCredits', { credits: number(plan.renewalCredits), count: plan.renewalCredits }) }}</p>
|
|
94
|
+
<v-btn variant="flat" color="primary" :disabled="disabled || hasSubscription || plan.available !== true" :aria-label="t('jskit.payments.account.choosePlanNamed', { plan: plan.name })" @click="checkout(plan)">{{ t('jskit.payments.account.choosePlan') }}</v-btn>
|
|
95
|
+
<p v-if="plan.available !== true">{{ t('jskit.payments.account.notAvailableForCheckout') }}</p>
|
|
73
96
|
</li>
|
|
74
97
|
</ul>
|
|
75
|
-
<p v-else>
|
|
76
|
-
<section v-if="canReadHistory" aria-label="
|
|
98
|
+
<p v-else>{{ t('jskit.payments.account.noPlans') }}</p>
|
|
99
|
+
<section v-if="canReadHistory" :aria-label="t('jskit.payments.account.history.region')" :aria-busy="historyLoading">
|
|
77
100
|
<div class="payment-account__actions">
|
|
78
|
-
<v-btn variant="tonal" :disabled="historyLoading || loading" @click="loadHistory('transactions')">
|
|
79
|
-
<v-btn variant="tonal" :disabled="historyLoading || loading" @click="loadHistory('subscriptions')">
|
|
101
|
+
<v-btn variant="tonal" :disabled="historyLoading || loading" @click="loadHistory('transactions')">{{ t('jskit.payments.account.history.viewTransactions') }}</v-btn>
|
|
102
|
+
<v-btn variant="tonal" :disabled="historyLoading || loading" @click="loadHistory('subscriptions')">{{ t('jskit.payments.account.history.viewSubscriptions') }}</v-btn>
|
|
80
103
|
</div>
|
|
81
104
|
<v-skeleton-loader v-if="historyLoading" type="list-item-three-line, list-item-three-line" />
|
|
82
105
|
<div v-else-if="historyError" role="alert">
|
|
83
106
|
<p>{{ historyError }}</p>
|
|
84
|
-
<v-btn variant="text" @click="emit('retry-history')">
|
|
107
|
+
<v-btn variant="text" @click="emit('retry-history')">{{ t('jskit.payments.account.history.retry') }}</v-btn>
|
|
85
108
|
</div>
|
|
86
109
|
<template v-else-if="history">
|
|
87
|
-
<p v-if="history.collection === 'transactions'">
|
|
88
|
-
<v-list v-if="history.items.length" lines="three" aria-label="
|
|
110
|
+
<p v-if="history.collection === 'transactions'">{{ t('jskit.payments.account.history.unpaidNote') }}</p>
|
|
111
|
+
<v-list v-if="history.items.length" lines="three" :aria-label="t('jskit.payments.account.history.records')">
|
|
89
112
|
<v-list-item v-for="item in history.items" :key="item.id">
|
|
90
|
-
<v-list-item-title>{{ item
|
|
91
|
-
<v-list-item-subtitle>{{
|
|
92
|
-
<p v-if="item.kind !== 'subscription'">
|
|
113
|
+
<v-list-item-title>{{ historyTitle(item) }}</v-list-item-title>
|
|
114
|
+
<v-list-item-subtitle>{{ t('jskit.payments.account.history.statusAndDate', { status: statusLabel(item.status), date: date(Date.parse(item.createdAt)) }) }}</v-list-item-subtitle>
|
|
115
|
+
<p v-if="item.kind !== 'subscription'">{{ historyTotal(item) }}</p>
|
|
93
116
|
</v-list-item>
|
|
94
117
|
</v-list>
|
|
95
|
-
<p v-else>
|
|
96
|
-
<v-btn v-if="history.nextCursor" variant="text" @click="loadHistory(history.collection, history.nextCursor)">
|
|
118
|
+
<p v-else>{{ t('jskit.payments.account.history.empty') }}</p>
|
|
119
|
+
<v-btn v-if="history.nextCursor" variant="text" @click="loadHistory(history.collection, history.nextCursor)">{{ t('jskit.payments.account.history.nextPage') }}</v-btn>
|
|
97
120
|
</template>
|
|
98
121
|
</section>
|
|
99
122
|
</template>
|
|
100
|
-
<p v-else>
|
|
123
|
+
<p v-else>{{ t('jskit.payments.account.unavailable') }}</p>
|
|
101
124
|
</section>
|
|
102
125
|
</template>
|
|
103
126
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { evaluateI18nCatalogContract } from "../../../tooling/test-support/i18nCatalogContract.mjs";
|
|
6
|
+
|
|
7
|
+
const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const REPO_ROOT = path.resolve(TEST_DIRECTORY, "..", "..", "..");
|
|
9
|
+
|
|
10
|
+
test("payments-web English catalog matches its source", () => {
|
|
11
|
+
const { problems } = evaluateI18nCatalogContract({
|
|
12
|
+
repoRoot: REPO_ROOT,
|
|
13
|
+
packageDir: path.join(REPO_ROOT, "packages", "payments-web")
|
|
14
|
+
});
|
|
15
|
+
assert.deepEqual(problems, []);
|
|
16
|
+
});
|
|
@@ -14,7 +14,9 @@ test('billing component preserves read state and guards checkout while leaving a
|
|
|
14
14
|
let app;
|
|
15
15
|
try {
|
|
16
16
|
const modulePath = `${directory}/component.mjs`;
|
|
17
|
-
|
|
17
|
+
// The compiled module lives in a temporary directory, so point its catalog import at the package.
|
|
18
|
+
const i18nModule = new URL('../src/client/i18n.js', import.meta.url).href;
|
|
19
|
+
await writeFile(modulePath, compiled.content.replace('"../i18n.js"', JSON.stringify(i18nModule)));
|
|
18
20
|
const { default: PaymentAccount } = await import(pathToFileURL(modulePath));
|
|
19
21
|
const node = (type, text = '') => ({ type, text, props: {}, children: [], parent: null });
|
|
20
22
|
const renderer = createRenderer({
|