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

Commit4aab23e

Browse files
committed
chore: turn e2e enterprise tests into e2e premium tests
1 parentccbb687 commit4aab23e

21 files changed

+201
-137
lines changed

‎.github/workflows/ci.yaml

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -499,19 +499,19 @@ jobs:
499499
working-directory:site
500500

501501
test-e2e:
502-
runs-on:${{ github.repository_owner == 'coder' && (matrix.variant.enterprise && 'depot-ubuntu-22.04' || 'depot-ubuntu-22.04-4') || 'ubuntu-latest' }}
503502
# test-e2e fails on 2-core 8GB runners, so we use the 4-core 16GB runner
503+
runs-on:${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }}
504504
needs:changes
505505
if:needs.changes.outputs.go == 'true' || needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
506506
timeout-minutes:20
507507
strategy:
508508
fail-fast:false
509509
matrix:
510510
variant:
511-
-enterprise:false
511+
-premium:false
512512
name:test-e2e
513-
-enterprise:true
514-
name:test-e2e-enterprise
513+
-premium:true
514+
name:test-e2e-premium
515515
name:${{ matrix.variant.name }}
516516
steps:
517517
-name:Checkout
@@ -535,38 +535,35 @@ jobs:
535535
-run:pnpm playwright:install
536536
working-directory:site
537537

538-
# Run tests that don't requirean enterprise license withoutan enterprise license
538+
# Run tests that don't requirea premium license withouta premium license
539539
-run:pnpm playwright:test --forbid-only --workers 1
540-
if:${{ !matrix.variant.enterprise }}
540+
if:${{ !matrix.variant.premium }}
541541
env:
542542
DEBUG:pw:api
543543
working-directory:site
544544

545-
# Run all of the tests withan enterprise license
545+
# Run all of the tests witha premium license
546546
-run:pnpm playwright:test --forbid-only --workers 1
547-
if:${{ matrix.variant.enterprise }}
547+
if:${{ matrix.variant.premium }}
548548
env:
549549
DEBUG:pw:api
550-
CODER_E2E_ENTERPRISE_LICENSE:${{ secrets.CODER_E2E_ENTERPRISE_LICENSE }}
551-
CODER_E2E_REQUIRE_ENTERPRISE_TESTS:"1"
550+
CODER_E2E_LICENSE:${{ secrets.CODER_E2E_LICENSE }}
551+
CODER_E2E_REQUIRE_PREMIUM_TESTS:"1"
552552
working-directory:site
553-
# Temporarily allow these to fail so that I can gather data about which
554-
# tests are failing.
555-
continue-on-error:true
556553

557554
-name:Upload Playwright Failed Tests
558555
if:always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork
559556
uses:actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9# v4.4.1
560557
with:
561-
name:failed-test-videos${{ matrix.variant.enterprise && '-enterprise' || '-agpl' }}
558+
name:failed-test-videos${{ matrix.variant.premium && '-premium' || '' }}
562559
path:./site/test-results/**/*.webm
563560
retention-days:7
564561

565562
-name:Upload pprof dumps
566563
if:always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork
567564
uses:actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9# v4.4.1
568565
with:
569-
name:debug-pprof-dumps${{ matrix.variant.enterprise && '-enterprise' || '-agpl' }}
566+
name:debug-pprof-dumps${{ matrix.variant.premium && '-premium' || '' }}
570567
path:./site/test-results/**/debug-pprof-*.txt
571568
retention-days:7
572569

‎site/e2e/constants.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const workspaceProxyPort = 3112;
1313
exportconstagentPProfPort=6061;
1414
exportconstcoderdPProfPort=6062;
1515

16+
// The name of the organization that should be used by default when needed.
17+
exportconstdefaultOrganizationName="coder";
18+
1619
// Credentials for the first user
1720
exportconstusername="admin";
1821
exportconstpassword="SomeSecurePassword!";
@@ -34,10 +37,22 @@ export const gitAuth = {
3437
installationsPath:"/installations",
3538
};
3639

37-
exportconstrequireEnterpriseTests=Boolean(
38-
process.env.CODER_E2E_REQUIRE_ENTERPRISE_TESTS,
40+
/**
41+
* Will make the tests fail if set to `true` and a license was not provided.
42+
*/
43+
exportconstpremiumTestsRequired=Boolean(
44+
process.env.CODER_E2E_REQUIRE_PREMIUM_TESTS,
3945
);
40-
exportconstenterpriseLicense=process.env.CODER_E2E_ENTERPRISE_LICENSE??"";
46+
47+
exportconstlicense=process.env.CODER_E2E_LICENSE??"";
48+
49+
/**
50+
* Certain parts of the UI change when organizations are enabled. Organizations
51+
* are enabled by a license entitlement, and license configuration is guaranteed
52+
* to run before any other tests, so having this as a bit of "global state" is
53+
* fine.
54+
*/
55+
exportconstorganizationsEnabled=Boolean(license);
4156

4257
// Disabling terraform tests is optional for environments without Docker + Terraform.
4358
// By default, we opt into these tests.

‎site/e2e/expectUrl.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ type PollingOptions = { timeout?: number; intervals?: number[] };
44

55
exportconstexpectUrl=expect.extend({
66
/**
7-
* toHavePathName is an alternative to `toHaveURL` that won't fail if the URL contains query parameters.
7+
* toHavePathName is an alternative to `toHaveURL` that won't fail if the URL
8+
* contains query parameters.
89
*/
910
asynctoHavePathName(page:Page,expected:string,options?:PollingOptions){
1011
letactual:string=newURL(page.url()).pathname;
@@ -34,4 +35,43 @@ export const expectUrl = expect.extend({
3435
)}\nActual:${this.utils.printReceived(actual)}`,
3536
};
3637
},
38+
39+
/**
40+
* toHavePathNameEndingWith allows checking the end of the URL (ie. to make
41+
* sure we redirected to a specific page) without caring about the entire URL,
42+
* which might depend on things like whether or not organizations or other
43+
* features are enabled.
44+
*/
45+
asynctoHavePathNameEndingWith(
46+
page:Page,
47+
expected:string,
48+
options?:PollingOptions,
49+
){
50+
letactual:string=newURL(page.url()).pathname;
51+
letpass:boolean;
52+
try{
53+
awaitexpect
54+
.poll(()=>{
55+
actual=newURL(page.url()).pathname;
56+
returnactual.endsWith(expected);
57+
},options)
58+
.toBe(true);
59+
pass=true;
60+
}catch{
61+
pass=false;
62+
}
63+
64+
return{
65+
name:"toHavePathNameEndingWith",
66+
pass,
67+
actual,
68+
expected,
69+
message:()=>
70+
`The page does not have the expected URL pathname.\nExpected a url${
71+
this.isNot ?"not " :""
72+
}ending with:${this.utils.printExpected(
73+
expected,
74+
)}\nActual:${this.utils.printReceived(actual)}`,
75+
};
76+
},
3777
});

‎site/e2e/global.setup.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@ test("setup deployment", async ({ page }) => {
2828
awaitpage.getByTestId("button-select-template").isVisible();
2929

3030
// Setup license
31-
if(constants.requireEnterpriseTests||constants.enterpriseLicense){
31+
if(constants.premiumTestsRequired||constants.license){
3232
// Make sure that we have something that looks like a real license
33-
expect(constants.enterpriseLicense).toBeTruthy();
34-
expect(constants.enterpriseLicense.length).toBeGreaterThan(92);// the signature alone should be this long
35-
expect(constants.enterpriseLicense.split(".").length).toBe(3);// otherwise it's invalid
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
3636

3737
awaitpage.goto("/deployment/licenses",{waitUntil:"domcontentloaded"});
3838

3939
awaitpage.getByText("Add a license").click();
40-
awaitpage.getByRole("textbox").fill(constants.enterpriseLicense);
40+
awaitpage.getByRole("textbox").fill(constants.license);
4141
awaitpage.getByText("Upload License").click();
4242

4343
awaitexpect(

‎site/e2e/helpers.ts

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import {
1616
agentPProfPort,
1717
coderMain,
1818
coderPort,
19-
enterpriseLicense,
19+
defaultOrganizationName,
20+
license,
21+
premiumTestsRequired,
2022
prometheusPort,
21-
requireEnterpriseTests,
2223
requireTerraformTests,
2324
}from"./constants";
2425
import{expectUrl}from"./expectUrl";
@@ -35,22 +36,28 @@ import {
3536
typeRichParameter,
3637
}from"./provisionerGenerated";
3738

38-
// requiresEnterpriseLicense will skip the test if we're not running with an enterprise license
39-
exportfunctionrequiresEnterpriseLicense(){
40-
if(requireEnterpriseTests){
39+
/**
40+
* requiresLicense will skip the test if we're not running with a license added
41+
*/
42+
exportfunctionrequiresLicense(){
43+
if(premiumTestsRequired){
4144
return;
4245
}
4346

44-
test.skip(!enterpriseLicense);
47+
test.skip(!license);
4548
}
4649

47-
// requireTerraformProvisioner by default is enabled.
50+
/**
51+
* requireTerraformProvisioner by default is enabled.
52+
*/
4853
exportfunctionrequireTerraformProvisioner(){
4954
test.skip(!requireTerraformTests);
5055
}
5156

52-
// createWorkspace creates a workspace for a template.
53-
// It does not wait for it to be running, but it does navigate to the page.
57+
/**
58+
* createWorkspace creates a workspace for a template. It does not wait for it
59+
* to be running, but it does navigate to the page.
60+
*/
5461
exportconstcreateWorkspace=async(
5562
page:Page,
5663
templateName:string,
@@ -90,7 +97,7 @@ export const createWorkspace = async (
9097

9198
awaitexpectUrl(page).toHavePathName(`/@admin/${name}`);
9299

93-
awaitpage.waitForSelector("*[data-testid='build-status'] >> text=Running",{
100+
awaitpage.waitForSelector("[data-testid='build-status'] >> text=Running",{
94101
state:"visible",
95102
});
96103
returnname;
@@ -151,8 +158,10 @@ export const verifyParameters = async (
151158
}
152159
};
153160

154-
// StarterTemplates are ids of starter templates that can be used in place of
155-
// the responses payload. These starter templates will require real provisioners.
161+
/**
162+
* StarterTemplates are ids of starter templates that can be used in place of
163+
* the responses payload. These starter templates will require real provisioners.
164+
*/
156165
exportenumStarterTemplates{
157166
STARTER_DOCKER="docker",
158167
}
@@ -166,11 +175,14 @@ function isStarterTemplate(
166175
returntypeofinput==="string";
167176
}
168177

169-
// createTemplate navigates to the /templates/new page and uploads a template
170-
// with the resources provided in the responses argument.
178+
/**
179+
* createTemplate navigates to the /templates/new page and uploads a template
180+
* with the resources provided in the responses argument.
181+
*/
171182
exportconstcreateTemplate=async(
172183
page:Page,
173184
responses?:EchoProvisionerResponses|StarterTemplates,
185+
orgName=defaultOrganizationName,
174186
):Promise<string>=>{
175187
letpath="/templates/new";
176188
if(isStarterTemplate(responses)){
@@ -191,31 +203,47 @@ export const createTemplate = async (
191203
});
192204
}
193205

206+
// If the organization picker is present on the page, select the default
207+
// organization.
208+
constorgPicker=page.getByLabel("Belongs to *");
209+
constorganizationsEnabled=awaitorgPicker.isVisible();
210+
if(organizationsEnabled){
211+
awaitorgPicker.click();
212+
awaitpage.getByText(orgName,{exact:true}).click();
213+
}
214+
194215
constname=randomName();
195216
awaitpage.getByLabel("Name *").fill(name);
196217
awaitpage.getByTestId("form-submit").click();
197-
awaitexpectUrl(page).toHavePathName(`/templates/${name}/files`,{
198-
timeout:30000,
199-
});
218+
awaitexpectUrl(page).toHavePathName(
219+
organizationsEnabled
220+
?`/templates/${orgName}/${name}/files`
221+
:`/templates/${name}/files`,
222+
{
223+
timeout:30000,
224+
},
225+
);
200226
returnname;
201227
};
202228

203-
// createGroup navigates to the /groups/create page and creates a group with a
204-
// random name.
229+
/**
230+
* createGroup navigates to the /groups/create page and creates a group with a
231+
* random name.
232+
*/
205233
exportconstcreateGroup=async(page:Page):Promise<string>=>{
206234
awaitpage.goto("/groups/create",{waitUntil:"domcontentloaded"});
207235
awaitexpectUrl(page).toHavePathName("/groups/create");
208236

209237
constname=randomName();
210238
awaitpage.getByLabel("Name",{exact:true}).fill(name);
211239
awaitpage.getByTestId("form-submit").click();
212-
awaitexpect(page).toHaveURL(
213-
/\/groups\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,
214-
);
240+
awaitexpectUrl(page).toHavePathName(`/groups/${name}`);
215241
returnname;
216242
};
217243

218-
// sshIntoWorkspace spawns a Coder SSH process and a client connected to it.
244+
/**
245+
* sshIntoWorkspace spawns a Coder SSH process and a client connected to it.
246+
*/
219247
exportconstsshIntoWorkspace=async(
220248
page:Page,
221249
workspace:string,
@@ -298,17 +326,21 @@ export const buildWorkspaceWithParameters = async (
298326
});
299327
};
300328

301-
// startAgent runs the coder agent with the provided token.
302-
// It awaits the agent to be ready before returning.
329+
/**
330+
* startAgent runs the coder agent with the provided token. It waits for the
331+
* agent to be ready before returning.
332+
*/
303333
exportconststartAgent=async(
304334
page:Page,
305335
token:string,
306336
):Promise<ChildProcess>=>{
307337
returnstartAgentWithCommand(page,token,"go","run",coderMain);
308338
};
309339

310-
// downloadCoderVersion downloads the version provided into a temporary dir and
311-
// caches it so subsequent calls are fast.
340+
/**
341+
* downloadCoderVersion downloads the version provided into a temporary dir and
342+
* caches it so subsequent calls are fast.
343+
*/
312344
exportconstdownloadCoderVersion=async(
313345
version:string,
314346
):Promise<string>=>{
@@ -448,8 +480,10 @@ interface EchoProvisionerResponses {
448480
apply?:RecursivePartial<Response>[];
449481
}
450482

451-
// createTemplateVersionTar consumes a series of echo provisioner protobufs and
452-
// converts it into an uploadable tar file.
483+
/**
484+
* createTemplateVersionTar consumes a series of echo provisioner protobufs and
485+
* converts it into an uploadable tar file.
486+
*/
453487
constcreateTemplateVersionTar=async(
454488
responses?:EchoProvisionerResponses,
455489
):Promise<Buffer>=>{
@@ -619,8 +653,10 @@ export const randomName = () => {
619653
returnrandomUUID().slice(0,8);
620654
};
621655

622-
// Awaiter is a helper that allows you to wait for a callback to be called.
623-
// It is useful for waiting for events to occur.
656+
/**
657+
* Awaiter is a helper that allows you to wait for a callback to be called. It
658+
* is useful for waiting for events to occur.
659+
*/
624660
exportclassAwaiter{
625661
privatepromise:Promise<void>;
626662
privatecallback?:()=>void;
@@ -825,7 +861,6 @@ export const updateTemplateSettings = async (
825861
awaitpage.goto(`/templates/${templateName}/settings`,{
826862
waitUntil:"domcontentloaded",
827863
});
828-
awaitexpectUrl(page).toHavePathName(`/templates/${templateName}/settings`);
829864

830865
for(const[key,value]ofObject.entries(templateSettingValues)){
831866
// Skip max_port_share_level for now since the frontend is not yet able to handle it
@@ -839,7 +874,7 @@ export const updateTemplateSettings = async (
839874
awaitpage.getByTestId("form-submit").click();
840875

841876
constname=templateSettingValues.name??templateName;
842-
awaitexpectUrl(page).toHavePathName(`/templates/${name}`);
877+
awaitexpectUrl(page).toHavePathNameEndingWith(`/${name}`);
843878
};
844879

845880
exportconstupdateWorkspace=async(

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp