-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: auto_login=off error on login and editing a user + FE tests (#3471)
* 🐛 (users.py): Fix issue where user password was not being updated correctly 📝 (constants.ts, authContext.tsx, index.tsx): Add LANGFLOW_REFRESH_TOKEN constant and update related code to support refresh token functionality 📝 (userManagementModal/index.tsx): Update form reset logic and handle input values correctly 📝 (LoginPage/index.tsx, LoginAdminPage/index.tsx): Update login function to include refresh token parameter 📝 (components/index.ts, auth.ts): Update inputHandlerEventType to support boolean values ✨ (auto-login-off.spec.ts): Add end-to-end test for user login functionality with auto_login set to false, CRUD operations for users, and verification of user flows visibility based on permissions. * ✨ (auto-login-off.spec.ts): improve test description for better clarity and understanding 📝 (auto-login-off.spec.ts): add comments to clarify the purpose of intercepting requests and performing CRUD operations * 🐛 (users.py): fix comparison of password to check for None using 'is not None' instead of '!= None' for better accuracy
- Loading branch information
1 parent
3a42b2b
commit b549913
Showing
11 changed files
with
296 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
import { expect, test } from "@playwright/test"; | ||
import { before, beforeEach } from "node:test"; | ||
|
||
test("when auto_login is false, admin can CRUD user's and should see just your own flows", async ({ | ||
page, | ||
}) => { | ||
await page.route("**/api/v1/auto_login", (route) => { | ||
route.fulfill({ | ||
status: 500, | ||
contentType: "application/json", | ||
body: JSON.stringify({ | ||
detail: { auto_login: false }, | ||
}), | ||
}); | ||
}); | ||
|
||
const randomName = Math.random().toString(36).substring(5); | ||
const randomPassword = Math.random().toString(36).substring(5); | ||
const secondRandomName = Math.random().toString(36).substring(5); | ||
const randomFlowName = Math.random().toString(36).substring(5); | ||
const secondRandomFlowName = Math.random().toString(36).substring(5); | ||
|
||
await page.goto("/"); | ||
|
||
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 }); | ||
|
||
await page.getByPlaceholder("Username").fill("langflow"); | ||
await page.getByPlaceholder("Password").fill("langflow"); | ||
|
||
await page.getByRole("button", { name: "Sign In" }).click(); | ||
|
||
await page.waitForSelector('[data-testid="mainpage_title"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
await page.waitForSelector('[id="new-project-btn"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
await page.getByTestId("user-profile-settings").click(); | ||
|
||
await page.getByText("Admin Page", { exact: true }).click(); | ||
|
||
//CRUD an user | ||
await page.getByText("New User", { exact: true }).click(); | ||
|
||
await page.getByPlaceholder("Username").last().fill(randomName); | ||
await page.locator('input[name="password"]').fill(randomPassword); | ||
await page.locator('input[name="confirmpassword"]').fill(randomPassword); | ||
|
||
await page.waitForTimeout(1000); | ||
|
||
await page.locator("#is_active").click(); | ||
|
||
await page.getByText("Save", { exact: true }).click(); | ||
|
||
await page.waitForSelector("text=new user added", { timeout: 30000 }); | ||
|
||
expect(await page.getByText(randomName, { exact: true }).isVisible()).toBe( | ||
true, | ||
); | ||
|
||
await page.getByTestId("icon-Trash2").last().click(); | ||
await page.getByText("Delete", { exact: true }).last().click(); | ||
|
||
await page.waitForSelector("text=user deleted", { timeout: 30000 }); | ||
|
||
expect(await page.getByText(randomName, { exact: true }).isVisible()).toBe( | ||
false, | ||
); | ||
|
||
await page.getByText("New User", { exact: true }).click(); | ||
|
||
await page.getByPlaceholder("Username").last().fill(randomName); | ||
await page.locator('input[name="password"]').fill(randomPassword); | ||
await page.locator('input[name="confirmpassword"]').fill(randomPassword); | ||
|
||
await page.waitForTimeout(1000); | ||
|
||
await page.locator("#is_active").click(); | ||
|
||
await page.getByText("Save", { exact: true }).click(); | ||
|
||
await page.waitForSelector("text=new user added", { timeout: 30000 }); | ||
|
||
await page.getByPlaceholder("Username").last().fill(randomName); | ||
|
||
await page.getByTestId("icon-Pencil").last().click(); | ||
|
||
await page.getByPlaceholder("Username").last().fill(secondRandomName); | ||
|
||
await page.getByText("Save", { exact: true }).click(); | ||
|
||
await page.waitForSelector("text=user edited", { timeout: 30000 }); | ||
|
||
await page.waitForTimeout(1000); | ||
|
||
expect( | ||
await page.getByText(secondRandomName, { exact: true }).isVisible(), | ||
).toBe(true); | ||
|
||
//user must see just your own flows | ||
await page.getByText("My Collection", { exact: true }).last().click(); | ||
|
||
await page.waitForSelector('[id="new-project-btn"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
let modalCount = 0; | ||
try { | ||
const modalTitleElement = await page?.getByTestId("modal-title"); | ||
if (modalTitleElement) { | ||
modalCount = await modalTitleElement.count(); | ||
} | ||
} catch (error) { | ||
modalCount = 0; | ||
} | ||
|
||
while (modalCount === 0) { | ||
await page.getByText("New Project", { exact: true }).click(); | ||
await page.waitForTimeout(3000); | ||
modalCount = await page.getByTestId("modal-title")?.count(); | ||
} | ||
|
||
await page.getByRole("heading", { name: "Basic Prompting" }).click(); | ||
|
||
await page.waitForSelector('[title="fit view"]', { | ||
timeout: 100000, | ||
}); | ||
|
||
await page.getByTitle("fit view").click(); | ||
await page.getByTitle("zoom out").click(); | ||
|
||
await page.getByTestId("flow-configuration-button").click(); | ||
await page.getByText("Settings", { exact: true }).last().click(); | ||
|
||
await page.getByPlaceholder("Flow Name").fill(randomFlowName); | ||
|
||
await page.getByText("Save", { exact: true }).click(); | ||
|
||
await page.waitForSelector('[data-testid="icon-ChevronLeft"]', { | ||
timeout: 100000, | ||
}); | ||
|
||
await page.getByTestId("icon-ChevronLeft").first().click(); | ||
|
||
await page.waitForSelector('[id="new-project-btn"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
expect( | ||
await page.getByText(randomFlowName, { exact: true }).last().isVisible(), | ||
).toBe(true); | ||
|
||
await page.getByTestId("user-profile-settings").click(); | ||
|
||
await page.getByText("Log Out", { exact: true }).click(); | ||
|
||
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 }); | ||
|
||
await page.getByPlaceholder("Username").fill(secondRandomName); | ||
await page.getByPlaceholder("Password").fill(randomPassword); | ||
await page.waitForTimeout(1000); | ||
|
||
await page.getByRole("button", { name: "Sign In" }).click(); | ||
|
||
await page.waitForSelector('[data-testid="mainpage_title"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
await page.waitForSelector('[id="new-project-btn"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
expect( | ||
( | ||
await page.waitForSelector("text=this folder is empty", { | ||
timeout: 30000, | ||
}) | ||
).isVisible(), | ||
); | ||
|
||
while (modalCount === 0) { | ||
await page.getByText("New Project", { exact: true }).click(); | ||
await page.waitForTimeout(3000); | ||
modalCount = await page.getByTestId("modal-title")?.count(); | ||
} | ||
|
||
await page.waitForSelector('[id="new-project-btn"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
await page.getByText("New Project", { exact: true }).click(); | ||
|
||
await page.getByRole("heading", { name: "Basic Prompting" }).click(); | ||
|
||
await page.waitForSelector('[title="fit view"]', { | ||
timeout: 100000, | ||
}); | ||
|
||
await page.getByTitle("fit view").click(); | ||
await page.getByTitle("zoom out").click(); | ||
|
||
await page.getByTestId("flow-configuration-button").click(); | ||
await page.getByText("Settings", { exact: true }).last().click(); | ||
|
||
await page.getByPlaceholder("Flow Name").fill(secondRandomFlowName); | ||
|
||
await page.getByText("Save", { exact: true }).click(); | ||
|
||
await page.waitForSelector('[data-testid="icon-ChevronLeft"]', { | ||
timeout: 100000, | ||
}); | ||
|
||
await page.getByTestId("icon-ChevronLeft").first().click(); | ||
|
||
expect( | ||
await page.getByText(secondRandomFlowName, { exact: true }).isVisible(), | ||
).toBe(true); | ||
expect( | ||
await page.getByText(randomFlowName, { exact: true }).isVisible(), | ||
).toBe(false); | ||
|
||
await page.getByTestId("user-profile-settings").click(); | ||
|
||
await page.getByText("Log Out", { exact: true }).click(); | ||
|
||
await page.waitForSelector("text=sign in to langflow", { timeout: 30000 }); | ||
|
||
await page.getByPlaceholder("Username").fill("langflow"); | ||
await page.getByPlaceholder("Password").fill("langflow"); | ||
|
||
await page.getByRole("button", { name: "Sign In" }).click(); | ||
|
||
await page.waitForSelector('[data-testid="mainpage_title"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
await page.waitForSelector('[id="new-project-btn"]', { | ||
timeout: 30000, | ||
}); | ||
|
||
expect( | ||
await page.getByText(secondRandomFlowName, { exact: true }).isVisible(), | ||
).toBe(false); | ||
expect( | ||
await page.getByText(randomFlowName, { exact: true }).isVisible(), | ||
).toBe(true); | ||
}); |
Oops, something went wrong.