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

Commit40fb57a

Browse files
authored
chore: turn e2e enterprise tests into e2e premium tests (#14979)
1 parent02f6203 commit40fb57a

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
@@ -549,19 +549,19 @@ jobs:
549549
working-directory:site
550550

551551
test-e2e:
552-
runs-on:${{ github.repository_owner == 'coder' && (matrix.variant.enterprise && 'depot-ubuntu-22.04' || 'depot-ubuntu-22.04-4') || 'ubuntu-latest' }}
553552
# test-e2e fails on 2-core 8GB runners, so we use the 4-core 16GB runner
553+
runs-on:${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }}
554554
needs:changes
555555
if:needs.changes.outputs.go == 'true' || needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
556556
timeout-minutes:20
557557
strategy:
558558
fail-fast:false
559559
matrix:
560560
variant:
561-
-enterprise:false
561+
-premium:false
562562
name:test-e2e
563-
-enterprise:true
564-
name:test-e2e-enterprise
563+
-premium:true
564+
name:test-e2e-premium
565565
name:${{ matrix.variant.name }}
566566
steps:
567567
-name:Harden Runner
@@ -590,38 +590,35 @@ jobs:
590590
-run:pnpm playwright:install
591591
working-directory:site
592592

593-
# Run tests that don't requirean enterprise license withoutan enterprise license
593+
# Run tests that don't requirea premium license withouta premium license
594594
-run:pnpm playwright:test --forbid-only --workers 1
595-
if:${{ !matrix.variant.enterprise }}
595+
if:${{ !matrix.variant.premium }}
596596
env:
597597
DEBUG:pw:api
598598
working-directory:site
599599

600-
# Run all of the tests withan enterprise license
600+
# Run all of the tests witha premium license
601601
-run:pnpm playwright:test --forbid-only --workers 1
602-
if:${{ matrix.variant.enterprise }}
602+
if:${{ matrix.variant.premium }}
603603
env:
604604
DEBUG:pw:api
605-
CODER_E2E_ENTERPRISE_LICENSE:${{ secrets.CODER_E2E_ENTERPRISE_LICENSE }}
606-
CODER_E2E_REQUIRE_ENTERPRISE_TESTS:"1"
605+
CODER_E2E_LICENSE:${{ secrets.CODER_E2E_LICENSE }}
606+
CODER_E2E_REQUIRE_PREMIUM_TESTS:"1"
607607
working-directory:site
608-
# Temporarily allow these to fail so that I can gather data about which
609-
# tests are failing.
610-
continue-on-error:true
611608

612609
-name:Upload Playwright Failed Tests
613610
if:always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork
614611
uses:actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9# v4.4.1
615612
with:
616-
name:failed-test-videos${{ matrix.variant.enterprise && '-enterprise' || '-agpl' }}
613+
name:failed-test-videos${{ matrix.variant.premium && '-premium' || '' }}
617614
path:./site/test-results/**/*.webm
618615
retention-days:7
619616

620617
-name:Upload pprof dumps
621618
if:always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork
622619
uses:actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9# v4.4.1
623620
with:
624-
name:debug-pprof-dumps${{ matrix.variant.enterprise && '-enterprise' || '-agpl' }}
621+
name:debug-pprof-dumps${{ matrix.variant.premium && '-premium' || '' }}
625622
path:./site/test-results/**/debug-pprof-*.txt
626623
retention-days:7
627624

‎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