Fix signup query (#1608)
* Remove emailVerified not null from query * Improve query to find existing user * Add test suite to Signup from a Team Invite * Allow importing modules with aliases * Delete created data after all tests * Resolve conflicts * Use teampro instead of pro user and refactor codepull/1618/head^2
parent
cad77ad237
commit
73de0c2185
|
@ -125,7 +125,7 @@ export default function MemberInvitationModal(props: { team: TeamWithMembers | n
|
|||
</p>
|
||||
)}
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<Button type="submit" color="primary" className="ml-2">
|
||||
<Button type="submit" color="primary" className="ml-2" data-testid="invite-new-member-button">
|
||||
{t("invite")}
|
||||
</Button>
|
||||
<Button type="button" color="secondary" onClick={props.onExit}>
|
||||
|
|
|
@ -71,7 +71,12 @@ export default function MemberListItem(props: Props) {
|
|||
/>
|
||||
<div className="inline-block ml-3">
|
||||
<span className="text-sm font-bold text-neutral-700">{name}</span>
|
||||
<span className="block -mt-1 text-xs text-gray-400">{props.member.email}</span>
|
||||
<span
|
||||
className="block -mt-1 text-xs text-gray-400"
|
||||
data-testid="member-email"
|
||||
data-email={props.member.email}>
|
||||
{props.member.email}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex mt-2 mr-2 sm:mt-0 sm:justify-center">
|
||||
|
|
|
@ -116,6 +116,7 @@
|
|||
"@types/jest": "^27.0.3",
|
||||
"@types/lodash": "^4.14.177",
|
||||
"@types/micro": "^7.3.6",
|
||||
"@types/module-alias": "^2.0.1",
|
||||
"@types/node": "^16.11.10",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/qrcode": "^1.4.1",
|
||||
|
@ -140,6 +141,7 @@
|
|||
"jest": "^26.0.0",
|
||||
"lint-staged": "^11.1.2",
|
||||
"mockdate": "^3.0.5",
|
||||
"module-alias": "^2.2.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.4",
|
||||
"prettier": "^2.3.2",
|
||||
|
|
|
@ -31,21 +31,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
return;
|
||||
}
|
||||
|
||||
// There is actually an existingUser if username matches
|
||||
// OR if email matches and both username and password are set
|
||||
const existingUser = await prisma.user.findFirst({
|
||||
where: {
|
||||
OR: [
|
||||
{ username },
|
||||
{
|
||||
username: username,
|
||||
},
|
||||
{
|
||||
email: userEmail,
|
||||
},
|
||||
],
|
||||
AND: [
|
||||
{
|
||||
emailVerified: {
|
||||
not: null,
|
||||
},
|
||||
AND: [{ email: userEmail }, { password: { not: null } }, { username: { not: null } }],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -72,7 +72,8 @@ export function TeamSettingsPage() {
|
|||
type="button"
|
||||
color="secondary"
|
||||
StartIcon={PlusIcon}
|
||||
onClick={() => setShowMemberInvitationModal(true)}>
|
||||
onClick={() => setShowMemberInvitationModal(true)}
|
||||
data-testid="new-member-button">
|
||||
{t("new_member")}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,17 @@
|
|||
import { PlaywrightTestConfig, devices } from "@playwright/test";
|
||||
import { addAliases } from "module-alias";
|
||||
|
||||
// Add aliases for the paths specified in the tsconfig.json file.
|
||||
// This is needed because playwright does not consider tsconfig.json
|
||||
// For more info, see:
|
||||
// https://stackoverflow.com/questions/69023682/typescript-playwright-error-cannot-find-module
|
||||
// https://github.com/microsoft/playwright/issues/7066#issuecomment-983984496
|
||||
addAliases({
|
||||
"@components": __dirname + "/components",
|
||||
"@lib": __dirname + "/lib",
|
||||
"@server": __dirname + "/server",
|
||||
"@ee": __dirname + "/ee",
|
||||
});
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
forbidOnly: !!process.env.CI,
|
||||
|
|
|
@ -1,5 +1,105 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { BASE_URL } from "@lib/config/constants";
|
||||
import prisma from "@lib/prisma";
|
||||
|
||||
import { todo } from "../lib/testUtils";
|
||||
|
||||
todo("Can signup from a team invite");
|
||||
test.describe("Can signup from a team invite", async () => {
|
||||
let page;
|
||||
let token: string | undefined;
|
||||
let signupFromInviteURL = "";
|
||||
const team = { name: "Seeded Team", slug: "seeded-team" };
|
||||
const testUser = {
|
||||
email: "test@test.com",
|
||||
password: "secretpassword123",
|
||||
validUsername: "test-user",
|
||||
};
|
||||
const usernameAlreadyTaken = "teampro";
|
||||
const emailAlreadyTaken = "teampro@example.com";
|
||||
|
||||
test.use({ storageState: "playwright/artifacts/teamproStorageState.json" });
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
|
||||
await page.goto("/settings/teams");
|
||||
|
||||
await page.waitForSelector(`a[title="${team.name}"]`);
|
||||
await page.click(`a[title="${team.name}"]`);
|
||||
|
||||
// Send invite to team
|
||||
await page.click('[data-testid="new-member-button"]');
|
||||
await page.fill('input[id="inviteUser"]', testUser.email);
|
||||
await page.click('[data-testid="invite-new-member-button"]');
|
||||
|
||||
// Wait for the invite to be sent
|
||||
await page.waitForSelector(`[data-testid="member-email"][data-email="${testUser.email}"]`);
|
||||
|
||||
const tokenObj = await prisma.verificationRequest.findFirst({
|
||||
where: { identifier: testUser.email },
|
||||
select: { token: true },
|
||||
});
|
||||
token = tokenObj?.token;
|
||||
signupFromInviteURL = `/auth/signup?token=${token}&callbackUrl=${BASE_URL}/settings/teams`;
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
// Delete test user
|
||||
await prisma.user.delete({
|
||||
where: { email: testUser.email },
|
||||
});
|
||||
// Delete verification request
|
||||
await prisma.verificationRequest.delete({
|
||||
where: { token },
|
||||
});
|
||||
});
|
||||
|
||||
test("Username already taken", async ({ page }) => {
|
||||
expect(token).toBeDefined();
|
||||
await page.goto(signupFromInviteURL);
|
||||
// Fill in form
|
||||
await page.fill('input[name="username"]', usernameAlreadyTaken);
|
||||
await page.fill('input[name="email"]', testUser.email);
|
||||
await page.fill('input[name="password"]', testUser.password);
|
||||
await page.fill('input[name="passwordcheck"]', testUser.password);
|
||||
await page.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit
|
||||
|
||||
await expect(page.locator('text="Username already taken"')).toBeVisible();
|
||||
});
|
||||
|
||||
test("Email address is already registered", async ({ page }) => {
|
||||
expect(token).toBeDefined();
|
||||
await page.goto(signupFromInviteURL);
|
||||
// Fill in form
|
||||
await page.fill('input[name="username"]', testUser.validUsername);
|
||||
await page.fill('input[name="email"]', emailAlreadyTaken);
|
||||
await page.fill('input[name="password"]', testUser.password);
|
||||
await page.fill('input[name="passwordcheck"]', testUser.password);
|
||||
await page.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit
|
||||
|
||||
await expect(page.locator('text="Email address is already registered"')).toBeVisible();
|
||||
});
|
||||
|
||||
test("Successful signup", async ({ page }) => {
|
||||
expect(token).toBeDefined();
|
||||
await page.goto(signupFromInviteURL);
|
||||
// Fill in form
|
||||
await page.fill('input[name="username"]', testUser.validUsername);
|
||||
await page.fill('input[name="email"]', testUser.email);
|
||||
await page.fill('input[name="password"]', testUser.password);
|
||||
await page.fill('input[name="passwordcheck"]', testUser.password);
|
||||
await page.press('input[name="passwordcheck"]', "Enter"); // Press Enter to submit
|
||||
|
||||
await page.waitForNavigation();
|
||||
|
||||
const createdUser = await prisma.user.findUnique({
|
||||
where: { email: testUser.email },
|
||||
});
|
||||
expect(createdUser).not.toBeNull();
|
||||
expect(createdUser?.username).toBe(testUser.validUsername);
|
||||
expect(createdUser?.password).not.toBeNull();
|
||||
expect(createdUser?.emailVerified).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
todo("Can login using 2FA");
|
||||
|
|
|
@ -33,7 +33,7 @@ async function globalSetup(/* config: FullConfig */) {
|
|||
await loginAsUser("free", browser);
|
||||
// await loginAsUser("usa", browser);
|
||||
// await loginAsUser("teamfree", browser);
|
||||
// await loginAsUser("teampro", browser);
|
||||
await loginAsUser("teampro", browser);
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -2589,6 +2589,11 @@
|
|||
"@types/node" "*"
|
||||
"@types/socket.io" "2.1.13"
|
||||
|
||||
"@types/module-alias@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/module-alias/-/module-alias-2.0.1.tgz#e5893236ce922152d57c5f3f978f764f4deeb45f"
|
||||
integrity sha512-DN/CCT1HQG6HquBNJdLkvV+4v5l/oEuwOHUPLxI+Eub0NED+lk0YUfba04WGH90EINiUrNgClkNnwGmbICeWMQ==
|
||||
|
||||
"@types/ms@*":
|
||||
version "0.7.31"
|
||||
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
|
||||
|
@ -7548,6 +7553,11 @@ mockdate@^3.0.5:
|
|||
resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb"
|
||||
integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==
|
||||
|
||||
module-alias@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0"
|
||||
integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==
|
||||
|
||||
mongodb-connection-string-url@^2.3.2:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.4.1.tgz#6b3c6c40133a0ad059fe9a0abda64b2a1cb4e8b4"
|
||||
|
|
Loading…
Reference in New Issue