1
1
import { type ChildProcess , exec , spawn } from "node:child_process" ;
2
2
import { randomUUID } from "node:crypto" ;
3
- import * as fs from "node:fs" ;
4
3
import net from "node:net" ;
5
4
import path from "node:path" ;
6
5
import { Duplex } from "node:stream" ;
@@ -19,10 +18,12 @@ import {
19
18
coderMain ,
20
19
coderPort ,
21
20
defaultOrganizationName ,
21
+ defaultPassword ,
22
22
license ,
23
23
premiumTestsRequired ,
24
24
prometheusPort ,
25
25
requireTerraformTests ,
26
+ users ,
26
27
} from "./constants" ;
27
28
import { expectUrl } from "./expectUrl" ;
28
29
import {
@@ -60,28 +61,75 @@ export function requireTerraformProvisioner() {
60
61
test . skip ( ! requireTerraformTests ) ;
61
62
}
62
63
64
+ type LoginOptions = {
65
+ username : string ;
66
+ email : string ;
67
+ password : string ;
68
+ } ;
69
+
70
+ export async function login ( page : Page , options : LoginOptions = users . admin ) {
71
+ const ctx = page . context ( ) ;
72
+ // biome-ignore lint/suspicious/noExplicitAny: reset the current user
73
+ ( ctx as any ) [ Symbol . for ( "currentUser" ) ] = undefined ;
74
+ await ctx . clearCookies ( ) ;
75
+ await page . goto ( "/login" ) ;
76
+ await page . getByLabel ( "Email" ) . fill ( options . email ) ;
77
+ await page . getByLabel ( "Password" ) . fill ( options . password ) ;
78
+ await page . getByRole ( "button" , { name : "Sign In" } ) . click ( ) ;
79
+ await expectUrl ( page ) . toHavePathName ( "/workspaces" ) ;
80
+ // biome-ignore lint/suspicious/noExplicitAny: update once logged in
81
+ ( ctx as any ) [ Symbol . for ( "currentUser" ) ] = options ;
82
+ }
83
+
84
+ export function currentUser ( page : Page ) : LoginOptions {
85
+ const ctx = page . context ( ) ;
86
+ // biome-ignore lint/suspicious/noExplicitAny: get the current user
87
+ const user = ( ctx as any ) [ Symbol . for ( "currentUser" ) ] ;
88
+
89
+ if ( ! user ) {
90
+ throw new Error ( "page context does not have a user. did you call `login`?" ) ;
91
+ }
92
+
93
+ return user ;
94
+ }
95
+
96
+ type CreateWorkspaceOptions = {
97
+ richParameters ?: RichParameter [ ] ;
98
+ buildParameters ?: WorkspaceBuildParameter [ ] ;
99
+ useExternalAuth ?: boolean ;
100
+ } ;
101
+
63
102
/**
64
103
* createWorkspace creates a workspace for a template. It does not wait for it
65
104
* to be running, but it does navigate to the page.
66
105
*/
67
106
export const createWorkspace = async (
68
107
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 = { } ,
73
110
) : Promise < string > => {
74
- await page . goto ( `/templates/${ templateName } /workspace` , {
111
+ const {
112
+ richParameters = [ ] ,
113
+ buildParameters = [ ] ,
114
+ useExternalAuth,
115
+ } = options ;
116
+
117
+ const templatePath =
118
+ typeof template === "string"
119
+ ? template
120
+ : `${ template . organization } /${ template . name } ` ;
121
+
122
+ await page . goto ( `/templates/${ templatePath } /workspace` , {
75
123
waitUntil : "domcontentloaded" ,
76
124
} ) ;
77
- await expectUrl ( page ) . toHavePathName ( `/templates/${ templateName } /workspace` ) ;
125
+ await expectUrl ( page ) . toHavePathName ( `/templates/${ templatePath } /workspace` ) ;
78
126
79
127
const name = randomName ( ) ;
80
128
await page . getByLabel ( "name" ) . fill ( name ) ;
81
129
82
130
await fillParameters ( page , richParameters , buildParameters ) ;
83
131
84
- if ( useExternalAuthProvider !== undefined ) {
132
+ if ( useExternalAuth ) {
85
133
// Create a new context for the popup which will be created when clicking the button
86
134
const popupPromise = page . waitForEvent ( "popup" ) ;
87
135
@@ -101,7 +149,9 @@ export const createWorkspace = async (
101
149
102
150
await page . getByTestId ( "form-submit" ) . click ( ) ;
103
151
104
- await expectUrl ( page ) . toHavePathName ( `/@admin/${ name } ` ) ;
152
+ const user = currentUser ( page ) ;
153
+
154
+ await expectUrl ( page ) . toHavePathName ( `/@${ user . username } /${ name } ` ) ;
105
155
106
156
await page . waitForSelector ( "[data-testid='build-status'] >> text=Running" , {
107
157
state : "visible" ,
@@ -214,6 +264,12 @@ export const createTemplate = async (
214
264
const orgPicker = page . getByLabel ( "Belongs to *" ) ;
215
265
const organizationsEnabled = await orgPicker . isVisible ( ) ;
216
266
if ( organizationsEnabled ) {
267
+ if ( orgName !== defaultOrganizationName ) {
268
+ throw new Error (
269
+ `No provisioners registered for ${ orgName } , creating this template will fail` ,
270
+ ) ;
271
+ }
272
+
217
273
await orgPicker . click ( ) ;
218
274
await page . getByText ( orgName , { exact : true } ) . click ( ) ;
219
275
}
@@ -659,8 +715,9 @@ const createTemplateVersionTar = async (
659
715
) ;
660
716
} ;
661
717
662
- export const randomName = ( ) => {
663
- return randomUUID ( ) . slice ( 0 , 8 ) ;
718
+ export const randomName = ( annotation ?: string ) => {
719
+ const base = randomUUID ( ) . slice ( 0 , 8 ) ;
720
+ return annotation ? `${ annotation } -${ base } ` : base ;
664
721
} ;
665
722
666
723
/**
@@ -1002,6 +1059,7 @@ type UserValues = {
1002
1059
username : string ;
1003
1060
email : string ;
1004
1061
password : string ;
1062
+ roles : string [ ] ;
1005
1063
} ;
1006
1064
1007
1065
export async function createUser (
@@ -1019,7 +1077,8 @@ export async function createUser(
1019
1077
const username = userValues . username ?? randomName ( ) ;
1020
1078
const name = userValues . name ?? username ;
1021
1079
const email = userValues . email ?? `${ username } @coder.com` ;
1022
- const password = userValues . password || "s3cure&password!" ;
1080
+ const password = userValues . password || defaultPassword ;
1081
+ const roles = userValues . roles ?? [ ] ;
1023
1082
1024
1083
await page . getByLabel ( "Username" ) . fill ( username ) ;
1025
1084
if ( name ) {
@@ -1036,10 +1095,18 @@ export async function createUser(
1036
1095
await expect ( page . getByText ( "Successfully created user." ) ) . toBeVisible ( ) ;
1037
1096
1038
1097
await expect ( page ) . toHaveTitle ( "Users - Coder" ) ;
1039
- await expect ( page . locator ( "tr" , { hasText : email } ) ) . toBeVisible ( ) ;
1098
+ const addedRow = page . locator ( "tr" , { hasText : email } ) ;
1099
+ await expect ( addedRow ) . toBeVisible ( ) ;
1100
+
1101
+ // Give them a role
1102
+ await addedRow . getByLabel ( "Edit user roles" ) . click ( ) ;
1103
+ for ( const role of roles ) {
1104
+ await page . getByText ( role , { exact : true } ) . click ( ) ;
1105
+ }
1106
+ await page . mouse . click ( 10 , 10 ) ; // close the popover by clicking outside of it
1040
1107
1041
1108
await page . goto ( returnTo , { waitUntil : "domcontentloaded" } ) ;
1042
- return { name, username, email, password } ;
1109
+ return { name, username, email, password, roles } ;
1043
1110
}
1044
1111
1045
1112
export async function createOrganization ( page : Page ) : Promise < {
0 commit comments