mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:07:31 +00:00
ci(e2e): Run Tests in Pipelines (#3903)
* cy10 changes
* test: setup local e2e env
* test(e2e): migrate e2e setup
* add more config
* make e2e setup work
* align variables
* fix config
* skip mfa
* set user register to false
* read ids from database if not provided
* don't read ids withing env file
* fix escaping in id queries
* fix project root
* export projectRoot path
* export projectRoot
* add e2e-setup.sh
* specify GOOS and GOARCH for dockerfile compatible binary
* add org default redirect uri
* correctly initialize org policy
* await ids
* fix awaiting ids
* fix cypress configuration
* fix some tests
* initial compose setup
* fix working directory
* fix references
* make tests less flaky
* run go tests
* compose works until e2e-setup incl
* pass created e2e sa key
* make cypress run
* derive e2e orgs domain from baseurl
* use host from baseurl for setup ctx
* move defaults.yaml back to cmd pkg
* just create org owner
* Don't render element if no roles are passed
* use map instead of switchMap
* fix e2e tests
* added testdata for e3e
* zipped dump
* removed dumpDir
* cypress workflow with compose
* quote name
* cleanup vars
* eliminate need for e2e setup
* compose has no builds anymore
* use compose run and zitadel nw
* test e2e on pr (#4114)
* test e2e on pr
* install goreleaser
* install npm dev dependencies
* run cypress wf
* dynamic release version
* skip flaky user tests
* skip flaky permissions test
* cache docker layers in pipeline
* Update .github/workflows/cypress.yml
Co-authored-by: Florian Forster <florian@caos.ch>
* align goreleaser version
* get rid of install.sh
* remove cypress-terminal-report
* Revert "remove cypress-terminal-report"
This reverts commit 254b5a1f87
.
* just one npm e2e:build command
* cache npm dependencies
* install node modules using docker
* dedicated e2e context
* fix syntax
* don't copy node modules from goreleaser
* add npm-copy target
* add tsconfig.json
* remove docker caching
* deleted unneeded shellscript
* naming and cleanup
Co-authored-by: Florian Forster <florian@caos.ch>
Co-authored-by: Christian Jakob <christian@caos.ch>
* cleanup
Co-authored-by: Elio Bischof <eliobischof@gmail.com>
Co-authored-by: Christian Jakob <christian@caos.ch>
Co-authored-by: Florian Forster <florian@caos.ch>
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"supportFile": "./cypress/support/index.ts",
|
||||
"reporter": "mochawesome",
|
||||
"reporterOptions": {
|
||||
"reportDir": "cypress/results",
|
||||
"overwrite": false,
|
||||
"html": true,
|
||||
"json": true
|
||||
},
|
||||
"chromeWebSecurity": false,
|
||||
"experimentalSessionSupport": true,
|
||||
"trashAssetsBeforeRuns": false
|
||||
}
|
||||
|
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ACTION=$1
|
||||
ENVFILE=$2
|
||||
|
||||
shift
|
||||
shift
|
||||
|
||||
projectRoot=".."
|
||||
|
||||
set -a; source $ENVFILE; set +a
|
||||
|
||||
NPX=""
|
||||
if ! command -v cypress &> /dev/null; then
|
||||
NPX="npx"
|
||||
fi
|
||||
|
||||
$NPX cypress $ACTION \
|
||||
--port ${E2E_CYPRESS_PORT} \
|
||||
--env org="${E2E_ORG}",org_owner_password="${E2E_ORG_OWNER_PW}",org_owner_viewer_password="${E2E_ORG_OWNER_VIEWER_PW}",org_project_creator_password="${E2E_ORG_PROJECT_CREATOR_PW}",login_policy_user_password="${E2E_LOGIN_POLICY_USER_PW}",password_complexity_user_password="${E2E_PASSWORD_COMPLEXITY_USER_PW}",consoleUrl=${E2E_CONSOLE_URL},apiUrl="${E2E_API_URL}",accountsUrl="${E2E_ACCOUNTS_URL}",issuerUrl="${E2E_ISSUER_URL}",serviceAccountKey="${E2E_SERVICEACCOUNT_KEY}",serviceAccountKeyPath="${E2E_SERVICEACCOUNT_KEY_PATH}",otherZitadelIdpInstance="${E2E_OTHER_ZITADEL_IDP_INSTANCE}",zitadelProjectResourceId="${E2E_ZITADEL_PROJECT_RESOURCE_ID}" \
|
||||
"$@"
|
4
console/cypress/.gitignore
vendored
4
console/cypress/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
results
|
||||
videos
|
||||
screenshots
|
||||
downloads
|
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 6.8 KiB |
@@ -1,50 +0,0 @@
|
||||
import { login, User } from "../../support/login/users";
|
||||
import { Apps, ensureProjectExists, ensureProjectResourceDoesntExist } from "../../support/api/projects";
|
||||
import { apiAuth } from "../../support/api/apiauth";
|
||||
|
||||
describe('applications', () => {
|
||||
|
||||
const testProjectName = 'e2eprojectapplication'
|
||||
const testAppName = 'e2eappundertest'
|
||||
|
||||
;[User.OrgOwner].forEach(user => {
|
||||
|
||||
describe(`as user "${user}"`, () => {
|
||||
|
||||
beforeEach(`ensure it doesn't exist already`, () => {
|
||||
login(user)
|
||||
apiAuth().then(api => {
|
||||
ensureProjectExists(api, testProjectName).then(projectID => {
|
||||
ensureProjectResourceDoesntExist(api, projectID, Apps, testAppName).then(() => {
|
||||
cy.visit(`${Cypress.env('consoleUrl')}/projects/${projectID}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('add app', () => {
|
||||
cy.get('mat-spinner')
|
||||
cy.get('mat-spinner').should('not.exist')
|
||||
cy.get('[data-e2e="app-card-add"]').should('be.visible').click()
|
||||
// select webapp
|
||||
cy.get('[formcontrolname="name"]').type(testAppName)
|
||||
cy.get('[for="WEB"]').click()
|
||||
cy.get('[data-e2e="continue-button-nameandtype"]').click()
|
||||
//select authentication
|
||||
cy.get('[for="PKCE"]').click()
|
||||
cy.get('[data-e2e="continue-button-authmethod"]').click()
|
||||
//enter URL
|
||||
cy.get('cnsl-redirect-uris').eq(0).type("https://testurl.org")
|
||||
cy.get('cnsl-redirect-uris').eq(1).type("https://testlogouturl.org")
|
||||
cy.get('[data-e2e="continue-button-redirecturis"]').click()
|
||||
cy.get('[data-e2e="create-button"]').click().then(() => {
|
||||
cy.get('[id*=overlay]').should('exist')
|
||||
})
|
||||
cy.get('.data-e2e-success')
|
||||
cy.wait(200)
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist')
|
||||
//TODO: check client ID/Secret
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,83 +0,0 @@
|
||||
import { apiAuth } from '../../support/api/apiauth';
|
||||
import { ensureHumanUserExists, ensureUserDoesntExist } from '../../support/api/users';
|
||||
import { login, User, username } from '../../support/login/users';
|
||||
|
||||
describe('humans', () => {
|
||||
const humansPath = `${Cypress.env('consoleUrl')}/users?type=human`;
|
||||
const testHumanUserNameAdd = 'e2ehumanusernameadd';
|
||||
const testHumanUserNameRemove = 'e2ehumanusernameremove';
|
||||
|
||||
[User.OrgOwner].forEach((user) => {
|
||||
describe(`as user "${user}"`, () => {
|
||||
beforeEach(() => {
|
||||
login(user);
|
||||
cy.visit(humansPath);
|
||||
cy.get('[data-cy=timestamp]');
|
||||
});
|
||||
|
||||
describe('add', () => {
|
||||
before(`ensure it doesn't exist already`, () => {
|
||||
apiAuth().then((apiCallProperties) => {
|
||||
ensureUserDoesntExist(apiCallProperties, testHumanUserNameAdd);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add a user', () => {
|
||||
cy.get('a[href="/users/create"]').click();
|
||||
cy.url().should('contain', 'users/create');
|
||||
cy.get('[formcontrolname="email"]').type(username('e2ehuman'));
|
||||
//force needed due to the prefilled username prefix
|
||||
cy.get('[formcontrolname="userName"]').type(testHumanUserNameAdd, { force: true });
|
||||
cy.get('[formcontrolname="firstName"]').type('e2ehumanfirstname');
|
||||
cy.get('[formcontrolname="lastName"]').type('e2ehumanlastname');
|
||||
cy.get('[formcontrolname="phone"]').type('+41 123456789');
|
||||
cy.get('[data-e2e="create-button"]').click();
|
||||
cy.get('.data-e2e-success');
|
||||
cy.wait(200);
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove', () => {
|
||||
before('ensure it exists', () => {
|
||||
apiAuth().then((api) => {
|
||||
ensureHumanUserExists(api, testHumanUserNameRemove);
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a human user', () => {
|
||||
cy.contains('tr', testHumanUserNameRemove, { timeout: 1000 })
|
||||
.find('button')
|
||||
//force due to angular hidden buttons
|
||||
.click({ force: true });
|
||||
cy.get('[e2e-data="confirm-dialog-input"]').type(username(testHumanUserNameRemove, Cypress.env('org')));
|
||||
cy.get('[e2e-data="confirm-dialog-button"]').click();
|
||||
cy.get('.data-e2e-success');
|
||||
cy.wait(200);
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
/*
|
||||
describe("users", ()=> {
|
||||
|
||||
before(()=> {
|
||||
cy.consolelogin(Cypress.env('username'), Cypress.env('password'), Cypress.env('consoleUrl'))
|
||||
})
|
||||
|
||||
it('should show personal information', () => {
|
||||
cy.log(`USER: show personal information`);
|
||||
//click on user information
|
||||
cy.get('a[href*="users/me"').eq(0).click()
|
||||
cy.url().should('contain', '/users/me')
|
||||
})
|
||||
|
||||
it('should show users', () => {
|
||||
cy.visit(Cypress.env('consoleUrl') + '/users/list/humans')
|
||||
cy.url().should('contain', 'users/list/humans')
|
||||
})
|
||||
})
|
||||
|
||||
*/
|
@@ -1,60 +0,0 @@
|
||||
import { apiAuth } from '../../support/api/apiauth';
|
||||
import { ensureMachineUserExists, ensureUserDoesntExist } from '../../support/api/users';
|
||||
import { login, User, username } from '../../support/login/users';
|
||||
|
||||
describe('machines', () => {
|
||||
const machinesPath = `${Cypress.env('consoleUrl')}/users?type=machine`;
|
||||
const testMachineUserNameAdd = 'e2emachineusernameadd';
|
||||
const testMachineUserNameRemove = 'e2emachineusernameremove';
|
||||
|
||||
[User.OrgOwner].forEach((user) => {
|
||||
describe(`as user "${user}"`, () => {
|
||||
beforeEach(() => {
|
||||
login(user);
|
||||
cy.visit(machinesPath);
|
||||
cy.get('[data-cy=timestamp]');
|
||||
});
|
||||
|
||||
describe('add', () => {
|
||||
before(`ensure it doesn't exist already`, () => {
|
||||
apiAuth().then((apiCallProperties) => {
|
||||
ensureUserDoesntExist(apiCallProperties, testMachineUserNameAdd);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add a machine', () => {
|
||||
cy.get('a[href="/users/create-machine"]').click();
|
||||
cy.url().should('contain', 'users/create-machine');
|
||||
//force needed due to the prefilled username prefix
|
||||
cy.get('[formcontrolname="userName"]').type(testMachineUserNameAdd, { force: true });
|
||||
cy.get('[formcontrolname="name"]').type('e2emachinename');
|
||||
cy.get('[formcontrolname="description"]').type('e2emachinedescription');
|
||||
cy.get('[data-e2e="create-button"]').click();
|
||||
cy.get('.data-e2e-success');
|
||||
cy.wait(200);
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove', () => {
|
||||
before('ensure it exists', () => {
|
||||
apiAuth().then((api) => {
|
||||
ensureMachineUserExists(api, testMachineUserNameRemove);
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a machine', () => {
|
||||
cy.contains('tr', testMachineUserNameRemove, { timeout: 1000 })
|
||||
.find('button')
|
||||
//force due to angular hidden buttons
|
||||
.click({ force: true });
|
||||
cy.get('[e2e-data="confirm-dialog-input"]').type(username(testMachineUserNameRemove, Cypress.env('org')));
|
||||
cy.get('[e2e-data="confirm-dialog-button"]').click();
|
||||
cy.get('.data-e2e-success');
|
||||
cy.wait(200);
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,120 +0,0 @@
|
||||
import { apiAuth, apiCallProperties } from "../../support/api/apiauth";
|
||||
import { ensureProjectExists, ensureProjectResourceDoesntExist, Roles } from "../../support/api/projects";
|
||||
import { login, User } from "../../support/login/users";
|
||||
|
||||
describe('permissions', () => {
|
||||
|
||||
const testProjectName = 'e2eprojectpermission'
|
||||
const testAppName = 'e2eapppermission'
|
||||
const testRoleName = 'e2eroleundertestname'
|
||||
const testRoleDisplay = 'e2eroleundertestdisplay'
|
||||
const testRoleGroup = 'e2eroleundertestgroup'
|
||||
const testGrantName = 'e2egrantundertest'
|
||||
|
||||
;[User.OrgOwner].forEach(user => {
|
||||
|
||||
describe(`as user "${user}"`, () => {
|
||||
|
||||
var api: apiCallProperties
|
||||
var projectId: number
|
||||
|
||||
beforeEach(() => {
|
||||
login(user)
|
||||
apiAuth().then(apiCalls => {
|
||||
api = apiCalls
|
||||
ensureProjectExists(apiCalls, testProjectName).then(projId => {
|
||||
projectId = projId
|
||||
cy.visit(`${Cypress.env('consoleUrl')}/projects/${projId}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('add role', () => {
|
||||
beforeEach(()=> {
|
||||
ensureProjectResourceDoesntExist(api, projectId, Roles, testRoleName)
|
||||
})
|
||||
|
||||
it('should add a role', () => {
|
||||
cy.get('[data-e2e="add-new-role"]').click()
|
||||
cy.get('[formcontrolname="key"]').type(testRoleName)
|
||||
cy.get('[formcontrolname="displayName"]').type(testRoleDisplay)
|
||||
cy.get('[formcontrolname="group"]').type(testRoleGroup)
|
||||
cy.get('[data-e2e="save-button"]').click()
|
||||
cy.get('.data-e2e-success')
|
||||
cy.wait(200)
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
/*
|
||||
|
||||
describe('permissions', () => {
|
||||
|
||||
before(()=> {
|
||||
// cy.consolelogin(Cypress.env('username'), Cypress.env('password'), Cypress.env('consoleUrl'))
|
||||
})
|
||||
|
||||
it('should show projects ', () => {
|
||||
cy.visit(Cypress.env('consoleUrl') + '/projects')
|
||||
cy.url().should('contain', '/projects')
|
||||
})
|
||||
|
||||
it('should add a role', () => {
|
||||
cy.visit(Cypress.env('consoleUrl') + '/org').then(() => {
|
||||
cy.url().should('contain', '/org');
|
||||
})
|
||||
cy.visit(Cypress.env('consoleUrl') + '/projects').then(() => {
|
||||
cy.url().should('contain', '/projects');
|
||||
cy.get('.card').should('contain.text', "newProjectToTest")
|
||||
})
|
||||
cy.get('.card').filter(':contains("newProjectToTest")').click()
|
||||
cy.get('.app-container').filter(':contains("newAppToTest")').should('be.visible').click()
|
||||
let projectID
|
||||
cy.url().then(url => {
|
||||
cy.log(url.split('/')[4])
|
||||
projectID = url.split('/')[4]
|
||||
});
|
||||
|
||||
cy.then(() => cy.visit(Cypress.env('consoleUrl') + '/projects/' + projectID +'/roles/create'))
|
||||
cy.get('[formcontrolname^=key]').type("newdemorole")
|
||||
cy.get('[formcontrolname^=displayName]').type("newdemodisplayname")
|
||||
cy.get('[formcontrolname^=group]').type("newdemogroupname")
|
||||
cy.get('button').filter(':contains("Save")').should('be.visible').click()
|
||||
//let the Role get processed
|
||||
cy.wait(5000)
|
||||
})
|
||||
|
||||
it('should add a grant', () => {
|
||||
cy.visit(Cypress.env('consoleUrl') + '/org').then(() => {
|
||||
cy.url().should('contain', '/org');
|
||||
})
|
||||
cy.visit(Cypress.env('consoleUrl') + '/projects').then(() => {
|
||||
cy.url().should('contain', '/projects');
|
||||
cy.get('.card').should('contain.text', "newProjectToTest")
|
||||
})
|
||||
cy.get('.card').filter(':contains("newProjectToTest")').click()
|
||||
cy.get('.app-container').filter(':contains("newAppToTest")').should('be.visible').click()
|
||||
let projectID
|
||||
cy.url().then(url => {
|
||||
cy.log(url.split('/')[4])
|
||||
projectID = url.split('/')[4]
|
||||
});
|
||||
|
||||
cy.then(() => cy.visit(Cypress.env('consoleUrl') + '/grant-create/project/' + projectID ))
|
||||
cy.get('input').type("demo")
|
||||
cy.get('[role^=listbox]').filter(`:contains("${Cypress.env("fullUserName")}")`).should('be.visible').click()
|
||||
cy.wait(5000)
|
||||
//cy.get('.button').contains('Continue').click()
|
||||
cy.get('button').filter(':contains("Continue")').click()
|
||||
cy.wait(5000)
|
||||
cy.get('tr').filter(':contains("demo")').find('label').click()
|
||||
cy.get('button').filter(':contains("Save")').should('be.visible').click()
|
||||
//let the grant get processed
|
||||
cy.wait(5000)
|
||||
})
|
||||
})
|
||||
|
||||
*/
|
@@ -1,78 +0,0 @@
|
||||
import { apiAuth } from '../../support/api/apiauth';
|
||||
import { ensureProjectDoesntExist, ensureProjectExists } from '../../support/api/projects';
|
||||
import { login, User } from '../../support/login/users';
|
||||
|
||||
describe('projects', () => {
|
||||
const testProjectNameCreate = 'e2eprojectcreate';
|
||||
const testProjectNameDeleteList = 'e2eprojectdeletelist';
|
||||
const testProjectNameDeleteGrid = 'e2eprojectdeletegrid';
|
||||
|
||||
[User.OrgOwner].forEach((user) => {
|
||||
describe(`as user "${user}"`, () => {
|
||||
beforeEach(() => {
|
||||
login(user);
|
||||
});
|
||||
|
||||
describe('add project', () => {
|
||||
beforeEach(`ensure it doesn't exist already`, () => {
|
||||
apiAuth().then((api) => {
|
||||
ensureProjectDoesntExist(api, testProjectNameCreate);
|
||||
});
|
||||
cy.visit(`${Cypress.env('consoleUrl')}/projects`);
|
||||
});
|
||||
|
||||
it('should add a project', () => {
|
||||
cy.get('.add-project-button').click({ force: true });
|
||||
cy.get('input').type(testProjectNameCreate);
|
||||
cy.get('[data-e2e="continue-button"]').click();
|
||||
cy.get('.data-e2e-success');
|
||||
cy.wait(200);
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove project', () => {
|
||||
describe('list view', () => {
|
||||
beforeEach('ensure it exists', () => {
|
||||
apiAuth().then((api) => {
|
||||
ensureProjectExists(api, testProjectNameDeleteList);
|
||||
});
|
||||
cy.visit(`${Cypress.env('consoleUrl')}/projects`);
|
||||
});
|
||||
|
||||
it('removes the project', () => {
|
||||
cy.get('[data-e2e=toggle-grid]').click();
|
||||
cy.get('[data-cy=timestamp]');
|
||||
cy.contains('tr', testProjectNameDeleteList, { timeout: 1000 })
|
||||
.find('[data-e2e=delete-project-button]')
|
||||
.click({ force: true });
|
||||
cy.get('[e2e-data="confirm-dialog-button"]').click();
|
||||
cy.get('.data-e2e-success');
|
||||
cy.wait(200);
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('grid view', () => {
|
||||
beforeEach('ensure it exists', () => {
|
||||
apiAuth().then((api) => {
|
||||
ensureProjectExists(api, testProjectNameDeleteGrid);
|
||||
});
|
||||
cy.visit(`${Cypress.env('consoleUrl')}/projects`);
|
||||
});
|
||||
|
||||
it('removes the project', () => {
|
||||
cy.contains('[data-e2e=grid-card]', testProjectNameDeleteGrid)
|
||||
.find('[data-e2e=delete-project-button]')
|
||||
.trigger('mouseover')
|
||||
.click();
|
||||
cy.get('[e2e-data="confirm-dialog-button"]').click();
|
||||
cy.get('.data-e2e-success');
|
||||
cy.wait(200);
|
||||
cy.get('.data-e2e-failure', { timeout: 0 }).should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,45 +0,0 @@
|
||||
import { apiAuth } from "../../support/api/apiauth";
|
||||
import { ensureHumanUserExists } from "../../support/api/users";
|
||||
import { login, User } from "../../support/login/users";
|
||||
|
||||
describe("login policy", ()=> {
|
||||
|
||||
const orgPath = `${Cypress.env('consoleUrl')}/org`
|
||||
|
||||
;[User.OrgOwner].forEach(user => {
|
||||
|
||||
describe(`as user "${user}"`, () => {
|
||||
|
||||
beforeEach(()=> {
|
||||
login(user)
|
||||
cy.visit(orgPath)
|
||||
// TODO: Why force?
|
||||
cy.contains('[data-e2e=policy-card]', 'Login Policy').contains('button', 'Modify').click({force: true}) // TODO: select data-e2e
|
||||
apiAuth().then(api => {
|
||||
ensureHumanUserExists(api, User.LoginPolicyUser)
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: verify email
|
||||
|
||||
it.skip(`username and password disallowed`, () => {
|
||||
login(User.LoginPolicyUser, "123abcABC?&*")
|
||||
})
|
||||
it(`registering is allowed`)
|
||||
it(`registering is disallowed`)
|
||||
it(`login by an external IDP is allowed`)
|
||||
it(`login by an external IDP is disallowed`)
|
||||
it(`MFA is forced`)
|
||||
it(`MFA is not forced`)
|
||||
it(`the password reset option is hidden`)
|
||||
it(`the password reset option is shown`)
|
||||
it(`passwordless login is allowed`)
|
||||
it(`passwordless login is disallowed`)
|
||||
describe('identity providers', () => {
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -1,34 +0,0 @@
|
||||
import { login, User } from "../../support/login/users";
|
||||
|
||||
describe("password complexity", ()=> {
|
||||
|
||||
const orgPath = `${Cypress.env('consoleUrl')}/org`
|
||||
const testProjectName = 'e2eproject'
|
||||
|
||||
;[User.OrgOwner].forEach(user => {
|
||||
|
||||
describe(`as user "${user}"`, () => {
|
||||
|
||||
beforeEach(()=> {
|
||||
login(user)
|
||||
cy.visit(orgPath)
|
||||
// TODO: Why force?
|
||||
cy.contains('[data-e2e=policy-card]', 'Password Complexity').contains('button', 'Modify').click({force: true}) // TODO: select data-e2e
|
||||
})
|
||||
|
||||
// TODO: fix saving password complexity policy bug
|
||||
|
||||
it(`should restrict passwords that don't have the minimal length`)
|
||||
it(`should require passwords to contain a number if option is switched on`)
|
||||
it(`should not require passwords to contain a number if option is switched off`)
|
||||
it(`should require passwords to contain a symbol if option is switched on`)
|
||||
it(`should not require passwords to contain a symbol if option is switched off`)
|
||||
it(`should require passwords to contain a lowercase letter if option is switched on`)
|
||||
it(`should not require passwords to contain a lowercase letter if option is switched off`)
|
||||
it(`should require passwords to contain an uppercase letter if option is switched on`)
|
||||
it(`should not require passwords to contain an uppercase letter if option is switched off`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -1,85 +0,0 @@
|
||||
import { apiAuth, apiCallProperties } from "../../support/api/apiauth";
|
||||
import { Policy, resetPolicy } from "../../support/api/policies";
|
||||
import { login, User } from "../../support/login/users";
|
||||
|
||||
describe("private labeling", ()=> {
|
||||
|
||||
const orgPath = `${Cypress.env('consoleUrl')}/org`
|
||||
|
||||
;[User.OrgOwner].forEach(user => {
|
||||
|
||||
describe(`as user "${user}"`, () => {
|
||||
|
||||
let api: apiCallProperties
|
||||
|
||||
|
||||
beforeEach(()=> {
|
||||
login(user)
|
||||
cy.visit(orgPath)
|
||||
// TODO: Why force?
|
||||
cy.contains('[data-e2e=policy-card]', 'Private Labeling').contains('button', 'Modify').click({force: true}) // TODO: select data-e2e
|
||||
})
|
||||
|
||||
customize('white', user)
|
||||
customize('dark', user)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
function customize(theme: string, user: User) {
|
||||
|
||||
describe(`${theme} theme`, () => {
|
||||
|
||||
beforeEach(() => {
|
||||
apiAuth().then(api => {
|
||||
resetPolicy(api, Policy.Label)
|
||||
})
|
||||
})
|
||||
|
||||
describe.skip('logo', () => {
|
||||
|
||||
beforeEach('expand logo category', () => {
|
||||
cy.contains('[data-e2e=policy-category]', 'Logo').click() // TODO: select data-e2e
|
||||
cy.fixture('logo.png').as('logo')
|
||||
})
|
||||
|
||||
it('should update a logo', () => {
|
||||
cy.get('[data-e2e=image-part-logo]').find('input').then(function (el) {
|
||||
const blob = Cypress.Blob.base64StringToBlob(this.logo, 'image/png')
|
||||
const file = new File([blob], 'images/logo.png', { type: 'image/png' })
|
||||
const list = new DataTransfer()
|
||||
|
||||
list.items.add(file)
|
||||
const myFileList = list.files
|
||||
|
||||
el[0].files = myFileList
|
||||
el[0].dispatchEvent(new Event('change', { bubbles: true }))
|
||||
})
|
||||
})
|
||||
it('should delete a logo')
|
||||
})
|
||||
it('should update an icon')
|
||||
it('should delete an icon')
|
||||
it.skip('should update the background color', () => {
|
||||
cy.contains('[data-e2e=color]', 'Background Color').find('button').click() // TODO: select data-e2e
|
||||
cy.get('color-editable-input').find('input').clear().type('#ae44dc')
|
||||
cy.get('[data-e2e=save-colors-button]').click()
|
||||
cy.get('[data-e2e=header-user-avatar]').click()
|
||||
cy.contains('Logout All Users').click() // TODO: select data-e2e
|
||||
login(User.LoginPolicyUser, true, null, () => {
|
||||
cy.pause()
|
||||
})
|
||||
})
|
||||
it('should update the primary color')
|
||||
it('should update the warning color')
|
||||
it('should update the font color')
|
||||
it('should update the font style')
|
||||
it('should hide the loginname suffix')
|
||||
it('should show the loginname suffix')
|
||||
it('should hide the watermark')
|
||||
it('should show the watermark')
|
||||
it('should show the current configuration')
|
||||
it('should reset the policy')
|
||||
})
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
module.exports = (on, config) => {
|
||||
|
||||
require('cypress-terminal-report/src/installLogsPrinter')(on);
|
||||
|
||||
config.defaultCommandTimeout = 10_000
|
||||
|
||||
config.env.parsedServiceAccountKey = config.env.serviceAccountKey
|
||||
if (config.env.serviceAccountKeyPath) {
|
||||
config.env.parsedServiceAccountKey = JSON.parse(readFileSync(config.env.serviceAccountKeyPath, 'utf-8'))
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
import { sign } from 'jsonwebtoken'
|
||||
|
||||
export interface apiCallProperties {
|
||||
authHeader: string
|
||||
mgntBaseURL: string
|
||||
}
|
||||
|
||||
export function apiAuth(): Cypress.Chainable<apiCallProperties> {
|
||||
const apiUrl = Cypress.env('apiUrl')
|
||||
const issuerUrl = Cypress.env('issuerUrl')
|
||||
const zitadelProjectResourceID = (<string>Cypress.env('zitadelProjectResourceId')).replace('bignumber-', '')
|
||||
|
||||
const key = Cypress.env("parsedServiceAccountKey")
|
||||
|
||||
const now = new Date().getTime()
|
||||
const iat = Math.floor(now / 1000)
|
||||
const exp = Math.floor(new Date(now + 1000 * 60 * 55).getTime() / 1000) // 55 minutes
|
||||
const bearerToken = sign({
|
||||
iss: key.userId,
|
||||
sub: key.userId,
|
||||
aud: `${issuerUrl}`,
|
||||
iat: iat,
|
||||
exp: exp
|
||||
}, key.key, {
|
||||
header: {
|
||||
alg: "RS256",
|
||||
kid: key.keyId
|
||||
}
|
||||
})
|
||||
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: `${apiUrl}/oauth/v2/token`,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: {
|
||||
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
||||
scope: `openid urn:zitadel:iam:org:project:id:${zitadelProjectResourceID}:aud`,
|
||||
assertion: bearerToken,
|
||||
}
|
||||
}).its('body.access_token').then(token => {
|
||||
|
||||
return <apiCallProperties>{
|
||||
authHeader: `Bearer ${token}`,
|
||||
mgntBaseURL: `${apiUrl}/management/v1/`,
|
||||
}
|
||||
})
|
||||
}
|
@@ -1,86 +0,0 @@
|
||||
import { apiCallProperties } from "./apiauth"
|
||||
|
||||
export function ensureSomethingExists(api: apiCallProperties, searchPath: string, find: (entity: any) => boolean, createPath: string, body: any): Cypress.Chainable<number> {
|
||||
|
||||
return searchSomething(api, searchPath, find).then(sRes => {
|
||||
if (sRes.entity) {
|
||||
return cy.wrap({
|
||||
id: sRes.entity.id,
|
||||
initialSequence: 0
|
||||
})
|
||||
}
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: `${api.mgntBaseURL}${createPath}`,
|
||||
headers: {
|
||||
Authorization: api.authHeader
|
||||
},
|
||||
body: body,
|
||||
failOnStatusCode: false,
|
||||
followRedirect: false,
|
||||
}).then(cRes => {
|
||||
expect(cRes.status).to.equal(200)
|
||||
return {
|
||||
id: cRes.body.id,
|
||||
initialSequence: sRes.sequence
|
||||
}
|
||||
})
|
||||
}).then((data) => {
|
||||
awaitDesired(30, (entity) => !!entity, data.initialSequence, api, searchPath, find)
|
||||
return cy.wrap<number>(data.id)
|
||||
})
|
||||
}
|
||||
|
||||
export function ensureSomethingDoesntExist(api: apiCallProperties, searchPath: string, find: (entity: any) => boolean, deletePath: (entity: any) => string): Cypress.Chainable<null> {
|
||||
|
||||
return searchSomething(api, searchPath, find).then(sRes => {
|
||||
if (!sRes.entity) {
|
||||
return cy.wrap(0)
|
||||
}
|
||||
return cy.request({
|
||||
method: 'DELETE',
|
||||
url: `${api.mgntBaseURL}${deletePath(sRes.entity)}`,
|
||||
headers: {
|
||||
Authorization: api.authHeader
|
||||
},
|
||||
failOnStatusCode: false
|
||||
}).then((dRes) => {
|
||||
expect(dRes.status).to.equal(200)
|
||||
return sRes.sequence
|
||||
})
|
||||
}).then((initialSequence) => {
|
||||
awaitDesired(30, (entity) => !entity , initialSequence, api, searchPath, find)
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
type SearchResult = {
|
||||
entity: any
|
||||
sequence: number
|
||||
}
|
||||
|
||||
function searchSomething(api: apiCallProperties, searchPath: string, find: (entity: any) => boolean): Cypress.Chainable<SearchResult> {
|
||||
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: `${api.mgntBaseURL}${searchPath}`,
|
||||
headers: {
|
||||
Authorization: api.authHeader
|
||||
},
|
||||
}).then(res => {
|
||||
return {
|
||||
entity: res.body.result?.find(find) || null,
|
||||
sequence: res.body.details.processedSequence
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function awaitDesired(trials: number, expectEntity: (entity: any) => boolean, initialSequence: number, api: apiCallProperties, searchPath: string, find: (entity: any) => boolean) {
|
||||
searchSomething(api, searchPath, find).then(resp => {
|
||||
if (!expectEntity(resp.entity) || resp.sequence <= initialSequence) {
|
||||
expect(trials, `trying ${trials} more times`).to.be.greaterThan(0);
|
||||
cy.wait(1000)
|
||||
awaitDesired(trials - 1, expectEntity, initialSequence, api, searchPath, find)
|
||||
}
|
||||
})
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
import { apiCallProperties } from "./apiauth"
|
||||
|
||||
|
||||
export enum Policy {
|
||||
Label = "label"
|
||||
}
|
||||
|
||||
export function resetPolicy(api: apiCallProperties, policy: Policy) {
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `${api.mgntBaseURL}/policies/${policy}`,
|
||||
headers: {
|
||||
Authorization: api.authHeader
|
||||
},
|
||||
}).then(res => {
|
||||
expect(res.status).to.equal(200)
|
||||
return null
|
||||
})
|
||||
}
|
@@ -1,80 +0,0 @@
|
||||
import { apiCallProperties } from "./apiauth"
|
||||
import { ensureSomethingDoesntExist, ensureSomethingExists } from "./ensure"
|
||||
|
||||
export function ensureProjectExists(api: apiCallProperties, projectName: string): Cypress.Chainable<number> {
|
||||
|
||||
return ensureSomethingExists(
|
||||
api,
|
||||
`projects/_search`,
|
||||
(project: any) => project.name === projectName,
|
||||
'projects',
|
||||
{ name: projectName },
|
||||
)
|
||||
}
|
||||
|
||||
export function ensureProjectDoesntExist(api: apiCallProperties, projectName: string): Cypress.Chainable<null> {
|
||||
|
||||
return ensureSomethingDoesntExist(
|
||||
api,
|
||||
`projects/_search`,
|
||||
(project: any) => project.name === projectName,
|
||||
(project) => `projects/${project.id}`,
|
||||
)
|
||||
}
|
||||
|
||||
class ResourceType {
|
||||
constructor(
|
||||
public resourcePath: string,
|
||||
public compareProperty: string,
|
||||
public identifierProperty: string,
|
||||
){}
|
||||
}
|
||||
|
||||
export const Apps = new ResourceType('apps', 'name', 'id')
|
||||
export const Roles = new ResourceType('roles', 'key', 'key')
|
||||
//export const Grants = new ResourceType('apps', 'name')
|
||||
|
||||
|
||||
export function ensureProjectResourceDoesntExist(api: apiCallProperties, projectId: number, resourceType: ResourceType, resourceName: string): Cypress.Chainable<null> {
|
||||
return ensureSomethingDoesntExist(
|
||||
api,
|
||||
`projects/${projectId}/${resourceType.resourcePath}/_search`,
|
||||
(resource: any) => {
|
||||
return resource[resourceType.compareProperty] === resourceName
|
||||
},
|
||||
(resource) => {
|
||||
return `projects/${projectId}/${resourceType.resourcePath}/${resource[resourceType.identifierProperty]}`
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function ensureApplicationExists(api: apiCallProperties, projectId: number, appName: string): Cypress.Chainable<number> {
|
||||
|
||||
return ensureSomethingExists(
|
||||
api,
|
||||
`projects/${projectId}/${Apps.resourcePath}/_search`,
|
||||
(resource: any) => resource.name === appName,
|
||||
`projects/${projectId}/${Apps.resourcePath}/oidc`,
|
||||
{
|
||||
name: appName,
|
||||
redirectUris: [
|
||||
'https://e2eredirecturl.org'
|
||||
],
|
||||
responseTypes: [
|
||||
"OIDC_RESPONSE_TYPE_CODE"
|
||||
],
|
||||
grantTypes: [
|
||||
"OIDC_GRANT_TYPE_AUTHORIZATION_CODE"
|
||||
],
|
||||
authMethodType: "OIDC_AUTH_METHOD_TYPE_NONE",
|
||||
postLogoutRedirectUris: [
|
||||
'https://e2elogoutredirecturl.org'
|
||||
],
|
||||
/* "clientId": "129383004379407963@e2eprojectpermission",
|
||||
"clockSkew": "0s",
|
||||
"allowedOrigins": [
|
||||
"https://testurl.org"
|
||||
]*/
|
||||
},
|
||||
)
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
import { apiCallProperties } from "./apiauth"
|
||||
import { ensureSomethingDoesntExist, ensureSomethingExists } from "./ensure"
|
||||
|
||||
export function ensureHumanUserExists(api: apiCallProperties, username: string): Cypress.Chainable<number> {
|
||||
|
||||
return ensureSomethingExists(
|
||||
api,
|
||||
'users/_search',
|
||||
(user: any) => user.userName === username,
|
||||
'users/human',
|
||||
{
|
||||
user_name: username,
|
||||
profile: {
|
||||
first_name: 'e2efirstName',
|
||||
last_name: 'e2elastName',
|
||||
},
|
||||
email: {
|
||||
email: 'e2e@email.ch',
|
||||
},
|
||||
phone: {
|
||||
phone: '+41 123456789',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function ensureMachineUserExists(api: apiCallProperties, username: string): Cypress.Chainable<number> {
|
||||
|
||||
return ensureSomethingExists(
|
||||
api,
|
||||
'users/_search',
|
||||
(user: any) => user.userName === username,
|
||||
'users/machine',
|
||||
{
|
||||
user_name: username,
|
||||
name: 'e2emachinename',
|
||||
description: 'e2emachinedescription',
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export function ensureUserDoesntExist(api: apiCallProperties, username: string): Cypress.Chainable<null> {
|
||||
|
||||
return ensureSomethingDoesntExist(
|
||||
api,
|
||||
'users/_search',
|
||||
(user: any) => user.userName === username,
|
||||
(user) => `users/${user.id}`
|
||||
)
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
*/
|
||||
/**
|
||||
* Custom command that authenticates a user.
|
||||
*
|
||||
* @example cy.consolelogin('hodor', 'hodor1234')
|
||||
*/
|
||||
/* consolelogin(username: string, password: string): void
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add('consolelogin', { prevSubject: false }, (username: string, password: string) => {
|
||||
|
||||
window.sessionStorage.removeItem("zitadel:access_token")
|
||||
cy.visit(Cypress.env('consoleUrl')).then(() => {
|
||||
// fill the fields and push button
|
||||
cy.get('#loginName').type(username, { log: false })
|
||||
cy.get('#submit-button').click()
|
||||
cy.get('#password').type(password, { log: false })
|
||||
cy.get('#submit-button').click()
|
||||
cy.location('pathname', {timeout: 5 * 1000}).should('eq', '/');
|
||||
})
|
||||
})
|
||||
*/
|
@@ -1,12 +0,0 @@
|
||||
require('cypress-terminal-report/src/installLogsCollector')();
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
||||
//import './commands'
|
||||
|
@@ -1,164 +0,0 @@
|
||||
export enum User {
|
||||
OrgOwner = 'org_owner',
|
||||
OrgOwnerViewer = 'org_owner_viewer',
|
||||
OrgProjectCreator = 'org_project_creator',
|
||||
LoginPolicyUser = 'login_policy_user',
|
||||
PasswordComplexityUser = 'password_complexity_user',
|
||||
IAMAdminUser = "zitadel-admin"
|
||||
}
|
||||
|
||||
export function login(user:User, force?: boolean, pw?: string, onUsernameScreen?: () => void, onPasswordScreen?: () => void, onAuthenticated?: () => void): void {
|
||||
let creds = credentials(user, pw)
|
||||
|
||||
const accountsUrl: string = Cypress.env('accountsUrl')
|
||||
const consoleUrl: string = Cypress.env('consoleUrl')
|
||||
const otherZitadelIdpInstance: boolean = Cypress.env('otherZitadelIdpInstance')
|
||||
|
||||
cy.session(creds.username, () => {
|
||||
|
||||
const cookies = new Map<string, string>()
|
||||
|
||||
if (otherZitadelIdpInstance) {
|
||||
cy.intercept({
|
||||
method: 'GET',
|
||||
url: `${accountsUrl}/login*`,
|
||||
times: 1
|
||||
}, (req) => {
|
||||
req.headers['cookie'] = requestCookies(cookies)
|
||||
req.continue((res) => {
|
||||
updateCookies(res.headers['set-cookie'] as string[], cookies)
|
||||
})
|
||||
}).as('login')
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: `${accountsUrl}/loginname*`,
|
||||
times: 1
|
||||
}, (req) => {
|
||||
req.headers['cookie'] = requestCookies(cookies)
|
||||
req.continue((res) => {
|
||||
updateCookies(res.headers['set-cookie'] as string[], cookies)
|
||||
})
|
||||
}).as('loginName')
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: `${accountsUrl}/password*`,
|
||||
times: 1
|
||||
}, (req) => {
|
||||
req.headers['cookie'] = requestCookies(cookies)
|
||||
req.continue((res) => {
|
||||
updateCookies(res.headers['set-cookie'] as string[], cookies)
|
||||
})
|
||||
}).as('password')
|
||||
|
||||
cy.intercept({
|
||||
method: 'GET',
|
||||
url: `${accountsUrl}/success*`,
|
||||
times: 1
|
||||
}, (req) => {
|
||||
req.headers['cookie'] = requestCookies(cookies)
|
||||
req.continue((res) => {
|
||||
updateCookies(res.headers['set-cookie'] as string[], cookies)
|
||||
})
|
||||
}).as('success')
|
||||
|
||||
cy.intercept({
|
||||
method: 'GET',
|
||||
url: `${accountsUrl}/oauth/v2/authorize/callback*`,
|
||||
times: 1
|
||||
}, (req) => {
|
||||
req.headers['cookie'] = requestCookies(cookies)
|
||||
req.continue((res) => {
|
||||
updateCookies(res.headers['set-cookie'] as string[], cookies)
|
||||
})
|
||||
}).as('callback')
|
||||
|
||||
cy.intercept({
|
||||
method: 'GET',
|
||||
url: `${accountsUrl}/oauth/v2/authorize*`,
|
||||
times: 1,
|
||||
}, (req) => {
|
||||
req.continue((res) => {
|
||||
updateCookies(res.headers['set-cookie'] as string[], cookies)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
cy.visit(`${consoleUrl}/loginname`, { retryOnNetworkFailure: true });
|
||||
|
||||
otherZitadelIdpInstance && cy.wait('@login')
|
||||
onUsernameScreen ? onUsernameScreen() : null
|
||||
cy.get('#loginName').type(creds.username)
|
||||
cy.get('#submit-button').click()
|
||||
|
||||
otherZitadelIdpInstance && cy.wait('@loginName')
|
||||
onPasswordScreen ? onPasswordScreen() : null
|
||||
cy.get('#password').type(creds.password)
|
||||
cy.get('#submit-button').click()
|
||||
|
||||
onAuthenticated ? onAuthenticated() : null
|
||||
|
||||
otherZitadelIdpInstance && cy.wait('@callback')
|
||||
|
||||
cy.location('pathname', {timeout: 5 * 1000}).should('eq', '/');
|
||||
|
||||
}, {
|
||||
validate: () => {
|
||||
|
||||
if (force) {
|
||||
throw new Error("clear session");
|
||||
}
|
||||
|
||||
cy.visit(`${consoleUrl}/users/me`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function username(withoutDomain: string, project?: string): string {
|
||||
return `${withoutDomain}@${project ? `${project}.` : ''}${host(Cypress.env('apiUrl')).replace('api.', '')}`
|
||||
}
|
||||
|
||||
function credentials(user: User, pw?: string) {
|
||||
const isAdmin = user == User.IAMAdminUser
|
||||
return {
|
||||
username: username(isAdmin ? user : `${user}_user_name`, isAdmin ? 'caos-ag' : Cypress.env('org')),
|
||||
password: pw ? pw : Cypress.env(`${user}_password`)
|
||||
}
|
||||
}
|
||||
|
||||
function updateCookies(newCookies: string[] | undefined, currentCookies: Map<string, string>) {
|
||||
if (newCookies === undefined) {
|
||||
return
|
||||
}
|
||||
newCookies.forEach(cs => {
|
||||
cs.split('; ').forEach(cookie => {
|
||||
const idx = cookie.indexOf('=')
|
||||
currentCookies.set(cookie.substring(0,idx), cookie.substring(idx+1))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function requestCookies(currentCookies: Map<string, string>): string[] {
|
||||
let list: Array<string> = []
|
||||
currentCookies.forEach((val, key) => {
|
||||
list.push(key+"="+val)
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
export function host(url: string): string {
|
||||
return stripPort(stripProtocol(url))
|
||||
}
|
||||
|
||||
function stripPort(s: string): string {
|
||||
const idx = s.indexOf(":")
|
||||
return idx === -1 ? s : s.substring(0,idx)
|
||||
}
|
||||
|
||||
function stripProtocol(url: string): string {
|
||||
return url.replace('http://', '').replace('https://', '')
|
||||
}
|
||||
|
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
E2E_CYPRESS_PORT=5003
|
||||
E2E_ORG=e2e-tests
|
||||
E2E_ORG_OWNER_PW=Password1!
|
||||
E2E_ORG_OWNER_VIEWER_PW=Password1!
|
||||
E2E_ORG_PROJECT_CREATOR_PW=Password1!
|
||||
E2E_PASSWORD_COMPLEXITY_USER_PW=Password1!
|
||||
E2E_LOGIN_POLICY_USER_PW=Password1!
|
||||
E2E_SERVICEACCOUNT_KEY_PATH="${projectRoot}/.keys/e2e.json"
|
||||
E2E_CONSOLE_URL="http://localhost:4200"
|
||||
E2E_API_URL="http://localhost:50002"
|
||||
E2E_ACCOUNTS_URL="http://localhost:50003"
|
||||
E2E_ISSUER_URL="http://localhost:50002/oauth/v2"
|
||||
E2E_OTHER_ZITADEL_IDP_INSTANCE=false
|
||||
E2E_ZITADEL_PROJECT_RESOURCE_ID="bignumber-$(echo -n $(docker compose -f ${projectRoot}/build/local/docker-compose-local.yml exec --no-TTY db cockroach sql --insecure --execute "select aggregate_id from eventstore.events where event_type = 'project.added' and event_data = '{\"name\": \"Zitadel\"}';" --format tsv) | cut -d " " -f 2)"
|
2897
console/package-lock.json
generated
2897
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,7 @@
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"prodbuild": "ng build --configuration production --base-href=/ui/console/",
|
||||
"lint": "ng lint && stylelint './src/**/*.scss' --syntax scss",
|
||||
"e2e": "./cypress.sh run e2e.env",
|
||||
"e2e:open": "./cypress.sh open e2e.env"
|
||||
"lint": "ng lint && stylelint './src/**/*.scss' --syntax scss"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@@ -68,18 +66,14 @@
|
||||
"@typescript-eslint/eslint-plugin": "5.30.4",
|
||||
"@typescript-eslint/parser": "5.30.4",
|
||||
"codelyzer": "^6.0.0",
|
||||
"cypress": "^10.1.0",
|
||||
"cypress-terminal-report": "^4.0.1",
|
||||
"eslint": "^8.18.0",
|
||||
"jasmine-core": "~4.2.0",
|
||||
"jasmine-spec-reporter": "~7.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "^2.0.0",
|
||||
"mochawesome": "^7.1.2",
|
||||
"prettier": "^2.4.1",
|
||||
"protractor": "~7.0.0",
|
||||
"stylelint": "^13.10.0",
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<span>ESC</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngSwitchCase="ActionKeysType.ADD" class="action-keys-row">
|
||||
<div *ngSwitchCase="ActionKeysType.ADD" class="action-keys-row" [attr.data-e2e]="'action-key-add'">
|
||||
<div class="action-key">
|
||||
<div class="key-overlay"></div>
|
||||
<span>N</span>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<p class="length">
|
||||
<span>{{ length }} </span>{{ 'PAGINATOR.COUNT' | translate }}
|
||||
</p>
|
||||
<p class="ts cnsl-secondary-text" *ngIf="timestamp">
|
||||
<p class="ts cnsl-secondary-text" *ngIf="timestamp" [attr.data-e2e]="'timestamp'">
|
||||
{{ timestamp | timestampToDate | localizedDate: 'EEEE dd. MMM YYYY, HH:mm' }}
|
||||
</p>
|
||||
</div>
|
||||
|
@@ -5,8 +5,8 @@
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['project.role.write:' + projectId, 'project.role.write']" actions>
|
||||
<a *ngIf="actionsVisible" [disabled]="disabled" [routerLink]="[ '/projects', projectId, 'roles', 'create']"
|
||||
color="primary" class="cnsl-action-button" mat-raised-button [attr.data-e2e]="'add-new-role'">
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
color="primary" class="cnsl-action-button" mat-raised-button>
|
||||
<mat-icon [attr.data-e2e]="'add-new-role'" class="icon">add</mat-icon>
|
||||
<span>{{ 'ACTIONS.NEW' | translate }}</span>
|
||||
<cnsl-action-keys (actionTriggered)="gotoRouterLink([ '/projects', projectId, 'roles', 'create'])">
|
||||
</cnsl-action-keys>
|
||||
|
@@ -3,7 +3,8 @@
|
||||
<ng-content select="[hoverActions]"></ng-content>
|
||||
<ng-content select="[actions]"></ng-content>
|
||||
<button (click)="$event.stopPropagation()" *ngIf="hasActions" class="table-actions-trigger" mat-icon-button
|
||||
[matMenuTriggerFor]="actions">
|
||||
[matMenuTriggerFor]="actions"
|
||||
[attr.data-e2e]="'table-actions-button'">
|
||||
<mat-icon>more_horiz</mat-icon>
|
||||
</button>
|
||||
|
||||
|
@@ -84,7 +84,8 @@
|
||||
<td mat-cell *matCellDef="let project">
|
||||
<cnsl-table-actions>
|
||||
<button actions *ngIf="project.id !== zitadelProjectId" color="warn" mat-icon-button
|
||||
matTooltip="{{'ACTIONS.DELETE' | translate}}" (click)="deleteProject(project.id, project.name)">
|
||||
matTooltip="{{'ACTIONS.DELETE' | translate}}" (click)="deleteProject(project.id, project.name)"
|
||||
[attr.data-e2e]="'delete-project-button'">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</cnsl-table-actions>
|
||||
|
@@ -13,11 +13,13 @@
|
||||
label="{{ 'USER.TABLE.TYPES.HUMAN' | translate }}"
|
||||
(clicked)="setType(Type.TYPE_HUMAN)"
|
||||
[active]="type === Type.TYPE_HUMAN"
|
||||
[attr.data-e2e]="'list-humans'"
|
||||
></cnsl-nav-toggle>
|
||||
<cnsl-nav-toggle
|
||||
label="{{ 'USER.TABLE.TYPES.MACHINE' | translate }}"
|
||||
(clicked)="setType(Type.TYPE_MACHINE)"
|
||||
[active]="type === Type.TYPE_MACHINE"
|
||||
[attr.data-e2e]="'list-machines'"
|
||||
></cnsl-nav-toggle>
|
||||
</div>
|
||||
|
||||
@@ -56,6 +58,7 @@
|
||||
mat-raised-button
|
||||
[disabled]="!canWrite"
|
||||
class="cnsl-action-button"
|
||||
[attr.data-e2e]="'create-user-button'"
|
||||
>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
<span>{{ 'ACTIONS.NEW' | translate }}</span>
|
||||
@@ -194,6 +197,7 @@
|
||||
color="warn"
|
||||
(click)="deleteUser(user)"
|
||||
[disabled]="!canWrite || !canDelete"
|
||||
[attr.e2e-data]="!canWrite || !canDelete ? 'disabled-delete-button' : 'enabled-delete-button'"
|
||||
mat-icon-button
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
|
Reference in New Issue
Block a user