(core) Customizable stripe plans.

Summary:
- Reading plans from Stripe, and allowing Stripe to define custom plans.
- Storing product features (aka limits) in Stripe, that override those in db.
- Adding hierarchical data in Stripe. All features are defined at Product level but can be overwritten on Price levels.
- New options for Support user to
-- Override product for team site (if he is added as a billing manager)
-- Override subscription and customer id for a team site
-- Attach an "offer", an custom plan configured in stripe that a team site can use
-- Enabling wire transfer for subscription by allowing subscription to be created without a payment method (which is customizable)

Test Plan: Updated and new.

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D4201
This commit is contained in:
Jarosław Sadziński
2024-05-17 21:14:34 +02:00
parent ed9514bae0
commit 60423edc17
40 changed files with 720 additions and 248 deletions

View File

@@ -236,7 +236,7 @@ export async function getDocWorkerUrl(): Promise<string> {
}
export async function waitForUrl(pattern: RegExp|string, waitMs: number = 2000) {
await driver.wait(() => testCurrentUrl(pattern), waitMs);
await driver.wait(() => testCurrentUrl(pattern), waitMs, `waiting for url ${pattern}`);
}
@@ -1823,6 +1823,34 @@ export async function editOrgAcls(): Promise<void> {
await driver.findWait('.test-um-members', 3000);
}
export async function addUser(email: string|string[], role?: 'Owner'|'Viewer'|'Editor'): Promise<void> {
await driver.findWait('.test-user-icon', 5000).click();
await driver.find('.test-dm-org-access').click();
await driver.findWait('.test-um-members', 500);
const orgInput = await driver.find('.test-um-member-new input');
const emails = Array.isArray(email) ? email : [email];
for(const e of emails) {
await orgInput.sendKeys(e, Key.ENTER);
if (role && role !== 'Viewer') {
await driver.findContentWait('.test-um-member', e, 1000).find('.test-um-member-role').click();
await driver.findContent('.test-um-role-option', role ?? 'Viewer').click();
}
}
await driver.find('.test-um-confirm').click();
await driver.wait(async () => !await driver.find('.test-um-members').isPresent(), 500);
}
export async function removeUser(email: string): Promise<void> {
await driver.findWait('.test-user-icon', 5000).click();
await driver.find('.test-dm-org-access').click();
await driver.findWait('.test-um-members', 500);
const kiwiRow = await driver.findContent('.test-um-member', email);
await kiwiRow.find('.test-um-member-delete').click();
await driver.find('.test-um-confirm').click();
await driver.wait(async () => !await driver.find('.test-um-members').isPresent(), 500);
}
/**
* Click confirm on a user manager dialog. If clickRemove is set, then
* any extra modal that pops up will be accepted. Returns true unless
@@ -3746,6 +3774,23 @@ export function findValue(selector: string, value: string|RegExp) {
return new WebElementPromise(driver, inner());
}
export async function switchUser(email: string) {
await driver.findWait('.test-user-icon', 1000).click();
await driver.findContentWait('.test-usermenu-other-email', exactMatch(email), 1000).click();
await waitForServer();
}
/**
* Waits for the toast message with the given text to appear.
*/
export async function waitForAccessDenied() {
await waitToPass(async () => {
assert.equal(
await driver.findWait('.test-notifier-toast-message', 1000).getText(),
'access denied');
});
}
} // end of namespace gristUtils
stackWrapOwnMethods(gristUtils);