Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit962608c

Browse files
authored
chore: allow signing in as non-admin users in e2e tests (#15892)
Closescoder/internal#168Gets rid of the "global state" authentication, and adds a `login` helperwhich should be called at the beginning of each test. This means thatnot every test needs to authenticated as admin, and we can even havetests that encompass multiple permission levels.We also now create more than just the single admin user during setup, sothat we can have a set of users to pick from as appropriate.
1 parent1ead56f commit962608c

File tree

40 files changed

+429
-155
lines changed

40 files changed

+429
-155
lines changed

‎site/e2e/api.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,9 @@ import { findSessionToken, randomName } from "./helpers";
99
letcurrentOrgId:string;
1010

1111
exportconstsetupApiCalls=async(page:Page)=>{
12-
try{
13-
consttoken=awaitfindSessionToken(page);
14-
API.setSessionToken(token);
15-
}catch{
16-
// If this fails, we have an unauthenticated client.
17-
}
18-
1912
API.setHost(`http://127.0.0.1:${coderPort}`);
13+
consttoken=awaitfindSessionToken(page);
14+
API.setSessionToken(token);
2015
};
2116

2217
exportconstgetCurrentOrgId=async():Promise<string>=>{

‎site/e2e/constants.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,30 @@ export const coderdPProfPort = 6062;
1515

1616
// The name of the organization that should be used by default when needed.
1717
exportconstdefaultOrganizationName="coder";
18+
exportconstdefaultPassword="SomeSecurePassword!";
1819

19-
// Credentials for the first user
20-
exportconstusername="admin";
21-
exportconstpassword="SomeSecurePassword!";
22-
exportconstemail="admin@coder.com";
20+
// Credentials for users
21+
exportconstusers={
22+
admin:{
23+
username:"admin",
24+
password:defaultPassword,
25+
email:"admin@coder.com",
26+
},
27+
auditor:{
28+
username:"auditor",
29+
password:defaultPassword,
30+
email:"auditor@coder.com",
31+
roles:["Template Admin","Auditor"],
32+
},
33+
user:{
34+
username:"user",
35+
password:defaultPassword,
36+
email:"user@coder.com",
37+
},
38+
}satisfiesRecord<
39+
string,
40+
{username:string;password:string;email:string;roles?:string[]}
41+
>;
2342

2443
exportconstgitAuth={
2544
deviceProvider:"device",

‎site/e2e/helpers.ts

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import{typeChildProcess,exec,spawn}from"node:child_process";
22
import{randomUUID}from"node:crypto";
3-
import*asfsfrom"node:fs";
43
importnetfrom"node:net";
54
importpathfrom"node:path";
65
import{Duplex}from"node:stream";
@@ -19,10 +18,12 @@ import {
1918
coderMain,
2019
coderPort,
2120
defaultOrganizationName,
21+
defaultPassword,
2222
license,
2323
premiumTestsRequired,
2424
prometheusPort,
2525
requireTerraformTests,
26+
users,
2627
}from"./constants";
2728
import{expectUrl}from"./expectUrl";
2829
import{
@@ -60,28 +61,75 @@ export function requireTerraformProvisioner() {
6061
test.skip(!requireTerraformTests);
6162
}
6263

64+
typeLoginOptions={
65+
username:string;
66+
email:string;
67+
password:string;
68+
};
69+
70+
exportasyncfunctionlogin(page:Page,options:LoginOptions=users.admin){
71+
constctx=page.context();
72+
// biome-ignore lint/suspicious/noExplicitAny: reset the current user
73+
(ctxasany)[Symbol.for("currentUser")]=undefined;
74+
awaitctx.clearCookies();
75+
awaitpage.goto("/login");
76+
awaitpage.getByLabel("Email").fill(options.email);
77+
awaitpage.getByLabel("Password").fill(options.password);
78+
awaitpage.getByRole("button",{name:"Sign In"}).click();
79+
awaitexpectUrl(page).toHavePathName("/workspaces");
80+
// biome-ignore lint/suspicious/noExplicitAny: update once logged in
81+
(ctxasany)[Symbol.for("currentUser")]=options;
82+
}
83+
84+
exportfunctioncurrentUser(page:Page):LoginOptions{
85+
constctx=page.context();
86+
// biome-ignore lint/suspicious/noExplicitAny: get the current user
87+
constuser=(ctxasany)[Symbol.for("currentUser")];
88+
89+
if(!user){
90+
thrownewError("page context does not have a user. did you call `login`?");
91+
}
92+
93+
returnuser;
94+
}
95+
96+
typeCreateWorkspaceOptions={
97+
richParameters?:RichParameter[];
98+
buildParameters?:WorkspaceBuildParameter[];
99+
useExternalAuth?:boolean;
100+
};
101+
63102
/**
64103
* createWorkspace creates a workspace for a template. It does not wait for it
65104
* to be running, but it does navigate to the page.
66105
*/
67106
exportconstcreateWorkspace=async(
68107
page:Page,
69-
templateName:string,
70-
richParameters:RichParameter[]=[],
71-
buildParameters:WorkspaceBuildParameter[]=[],
72-
useExternalAuthProvider:string|undefined=undefined,
108+
template:string|{organization:string;name:string},
109+
options:CreateWorkspaceOptions={},
73110
):Promise<string>=>{
74-
awaitpage.goto(`/templates/${templateName}/workspace`,{
111+
const{
112+
richParameters=[],
113+
buildParameters=[],
114+
useExternalAuth,
115+
}=options;
116+
117+
consttemplatePath=
118+
typeoftemplate==="string"
119+
?template
120+
:`${template.organization}/${template.name}`;
121+
122+
awaitpage.goto(`/templates/${templatePath}/workspace`,{
75123
waitUntil:"domcontentloaded",
76124
});
77-
awaitexpectUrl(page).toHavePathName(`/templates/${templateName}/workspace`);
125+
awaitexpectUrl(page).toHavePathName(`/templates/${templatePath}/workspace`);
78126

79127
constname=randomName();
80128
awaitpage.getByLabel("name").fill(name);
81129

82130
awaitfillParameters(page,richParameters,buildParameters);
83131

84-
if(useExternalAuthProvider!==undefined){
132+
if(useExternalAuth){
85133
// Create a new context for the popup which will be created when clicking the button
86134
constpopupPromise=page.waitForEvent("popup");
87135

@@ -101,7 +149,9 @@ export const createWorkspace = async (
101149

102150
awaitpage.getByTestId("form-submit").click();
103151

104-
awaitexpectUrl(page).toHavePathName(`/@admin/${name}`);
152+
constuser=currentUser(page);
153+
154+
awaitexpectUrl(page).toHavePathName(`/@${user.username}/${name}`);
105155

106156
awaitpage.waitForSelector("[data-testid='build-status'] >> text=Running",{
107157
state:"visible",
@@ -214,6 +264,12 @@ export const createTemplate = async (
214264
constorgPicker=page.getByLabel("Belongs to *");
215265
constorganizationsEnabled=awaitorgPicker.isVisible();
216266
if(organizationsEnabled){
267+
if(orgName!==defaultOrganizationName){
268+
thrownewError(
269+
`No provisioners registered for${orgName}, creating this template will fail`,
270+
);
271+
}
272+
217273
awaitorgPicker.click();
218274
awaitpage.getByText(orgName,{exact:true}).click();
219275
}
@@ -659,8 +715,9 @@ const createTemplateVersionTar = async (
659715
);
660716
};
661717

662-
exportconstrandomName=()=>{
663-
returnrandomUUID().slice(0,8);
718+
exportconstrandomName=(annotation?:string)=>{
719+
constbase=randomUUID().slice(0,8);
720+
returnannotation ?`${annotation}-${base}` :base;
664721
};
665722

666723
/**
@@ -1002,6 +1059,7 @@ type UserValues = {
10021059
username:string;
10031060
email:string;
10041061
password:string;
1062+
roles:string[];
10051063
};
10061064

10071065
exportasyncfunctioncreateUser(
@@ -1019,7 +1077,8 @@ export async function createUser(
10191077
constusername=userValues.username??randomName();
10201078
constname=userValues.name??username;
10211079
constemail=userValues.email??`${username}@coder.com`;
1022-
constpassword=userValues.password||"s3cure&password!";
1080+
constpassword=userValues.password||defaultPassword;
1081+
constroles=userValues.roles??[];
10231082

10241083
awaitpage.getByLabel("Username").fill(username);
10251084
if(name){
@@ -1036,10 +1095,18 @@ export async function createUser(
10361095
awaitexpect(page.getByText("Successfully created user.")).toBeVisible();
10371096

10381097
awaitexpect(page).toHaveTitle("Users - Coder");
1039-
awaitexpect(page.locator("tr",{hasText:email})).toBeVisible();
1098+
constaddedRow=page.locator("tr",{hasText:email});
1099+
awaitexpect(addedRow).toBeVisible();
1100+
1101+
// Give them a role
1102+
awaitaddedRow.getByLabel("Edit user roles").click();
1103+
for(constroleofroles){
1104+
awaitpage.getByText(role,{exact:true}).click();
1105+
}
1106+
awaitpage.mouse.click(10,10);// close the popover by clicking outside of it
10401107

10411108
awaitpage.goto(returnTo,{waitUntil:"domcontentloaded"});
1042-
return{ name, username, email, password};
1109+
return{ name, username, email, password, roles};
10431110
}
10441111

10451112
exportasyncfunctioncreateOrganization(page:Page):Promise<{

‎site/e2e/hooks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import http from "node:http";
22
importtype{BrowserContext,Page}from"@playwright/test";
33
import{coderPort,gitAuth}from"./constants";
44

5-
exportconstbeforeCoderTest=async(page:Page)=>{
5+
exportconstbeforeCoderTest=(page:Page)=>{
66
page.on("console",(msg)=>console.info(`[onConsole]${msg.text()}`));
77

88
page.on("request",(request)=>{

‎site/e2e/playwright.config.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ import {
1313

1414
exportconstwsEndpoint=process.env.CODER_E2E_WS_ENDPOINT;
1515

16-
// This is where auth cookies are stored!
17-
exportconststorageState=path.join(__dirname,".auth.json");
18-
1916
// If running terraform tests, verify the requirements exist in the
2017
// environment.
2118
//
@@ -58,13 +55,12 @@ export default defineConfig({
5855
projects:[
5956
{
6057
name:"testsSetup",
61-
testMatch:/global.setup\.ts/,
58+
testMatch:/setup\/.*\.spec\.ts/,
6259
},
6360
{
6461
name:"tests",
65-
testMatch:/.*\.spec\.ts/,
62+
testMatch:/tests\/.*\.spec\.ts/,
6663
dependencies:["testsSetup"],
67-
use:{ storageState},
6864
timeout:30_000,
6965
},
7066
],

‎site/e2e/global.setup.tsrenamed to‎site/e2e/setup/createUsers.spec.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import{expect,test}from"@playwright/test";
22
import{API}from"api/api";
33
import{Language}from"pages/CreateUserPage/CreateUserForm";
4-
import{setupApiCalls}from"./api";
5-
import*asconstantsfrom"./constants";
6-
import{expectUrl}from"./expectUrl";
7-
import{storageState}from"./playwright.config";
4+
import{coderPort,license,premiumTestsRequired,users}from"../constants";
5+
import{expectUrl}from"../expectUrl";
6+
import{createUser}from"../helpers";
87

98
test("setup deployment",async({ page})=>{
109
awaitpage.goto("/",{waitUntil:"domcontentloaded"});
11-
awaitsetupApiCalls(page);
10+
API.setHost(`http://127.0.0.1:${coderPort}`);
1211
constexists=awaitAPI.hasFirstUser();
1312
// First user already exists, abort early. All tests execute this as a dependency,
1413
// if you run multiple tests in the UI, this will fail unless we check this.
@@ -17,28 +16,35 @@ test("setup deployment", async ({ page }) => {
1716
}
1817

1918
// Setup first user
20-
awaitpage.getByLabel(Language.usernameLabel).fill(constants.username);
21-
awaitpage.getByLabel(Language.emailLabel).fill(constants.email);
22-
awaitpage.getByLabel(Language.passwordLabel).fill(constants.password);
19+
awaitpage.getByLabel(Language.usernameLabel).fill(users.admin.username);
20+
awaitpage.getByLabel(Language.emailLabel).fill(users.admin.email);
21+
awaitpage.getByLabel(Language.passwordLabel).fill(users.admin.password);
2322
awaitpage.getByTestId("create").click();
2423

2524
awaitexpectUrl(page).toHavePathName("/workspaces");
26-
awaitpage.context().storageState({path:storageState});
27-
2825
awaitpage.getByTestId("button-select-template").isVisible();
2926

27+
for(constuserofObject.values(users)){
28+
// Already created as first user
29+
if(user.username==="admin"){
30+
continue;
31+
}
32+
33+
awaitcreateUser(page,user);
34+
}
35+
3036
// Setup license
31-
if(constants.premiumTestsRequired||constants.license){
37+
if(premiumTestsRequired||license){
3238
// Make sure that we have something that looks like a real license
33-
expect(constants.license).toBeTruthy();
34-
expect(constants.license.length).toBeGreaterThan(92);// the signature alone should be this long
35-
expect(constants.license.split(".").length).toBe(3);// otherwise it's invalid
39+
expect(license).toBeTruthy();
40+
expect(license.length).toBeGreaterThan(92);// the signature alone should be this long
41+
expect(license.split(".").length).toBe(3);// otherwise it's invalid
3642

3743
awaitpage.goto("/deployment/licenses",{waitUntil:"domcontentloaded"});
3844
awaitexpect(page).toHaveTitle("License Settings - Coder");
3945

4046
awaitpage.getByText("Add a license").click();
41-
awaitpage.getByRole("textbox").fill(constants.license);
47+
awaitpage.getByRole("textbox").fill(license);
4248
awaitpage.getByText("Upload License").click();
4349

4450
awaitexpect(

‎site/e2e/tests/app.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ import { test } from "@playwright/test";
44
import{
55
createTemplate,
66
createWorkspace,
7+
login,
78
startAgent,
89
stopAgent,
910
stopWorkspace,
1011
}from"../helpers";
1112
import{beforeCoderTest}from"../hooks";
1213

13-
test.beforeEach(({ page})=>beforeCoderTest(page));
14+
test.beforeEach(async({ page})=>{
15+
beforeCoderTest(page);
16+
awaitlogin(page);
17+
});
1418

1519
test("app",async({ context, page})=>{
1620
test.setTimeout(75_000);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp