Skip to content
48 changes: 23 additions & 25 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ jobs:
- name: Save licenses
run: pnpm licenses:csv && pnpm licenses:save

- name: Check types
run: pnpm check:types
# - name: Check types
# run: pnpm check:types

- name: Check format
run: pnpm check:format
# - name: Check format
# run: pnpm check:format

- name: Lint
run: pnpm lint
# - name: Lint
# run: pnpm lint

- name: Run unit tests
run: pnpm test:unit --coverage
# - name: Run unit tests
# run: pnpm test:unit --coverage

- name: SonarCloud Scan
if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/')
Expand Down Expand Up @@ -75,32 +75,30 @@ jobs:
fail-fast: false
matrix:
suites:
- suite: part-1
test_suites: admin-settings,spaces,journeys
# - suite: part-1
# test_suites: admin-settings,spaces
- suite: part-2
test_suites: navigation,user-settings,app-store,file-action

- suite: part-3
test_suites: shares,search,runtime
tika: true
- suite: app-provider
test_suites: app-provider
collaboration: true
- suite: ocm
test_suites: ocm
federated: true
- suite: oidc
feature_files: specs/oidc/refreshToken.spec.ts
oidc: true
- suite: oidc-iframe
feature_files: specs/oidc/iframeTokenRenewal.spec.ts
oidc_iframe: true
- suite: smoke
test_suites: smoke
- suite: mfa
test_suites: mfa
mfa: true
# - suite: ocm
# test_suites: ocm
# federated: true
# - suite: oidc
# feature_files: specs/oidc/refreshToken.spec.ts
# oidc: true
# - suite: oidc-iframe
# feature_files: specs/oidc/iframeTokenRenewal.spec.ts
# oidc_iframe: true
# - suite: smoke
# test_suites: smoke
- suite: keycloak
feature_files: specs/admin-settings/spaces.spec.ts,specs/journeys/kindergarten.spec.ts, specs/keycloak/groups.spec.ts,specs/admin-settings/users.spec.ts:38,specs/admin-settings/users.spec.ts:75,specs/admin-settings/users.spec.ts:175,specs/admin-settings/users.spec.ts:210,specs/admin-settings/users.spec.ts:285
feature_files: specs/admin-settings/spaces.spec.ts,specs/admin-settings/users.spec.ts:38,specs/admin-settings/users.spec.ts:75,specs/admin-settings/users.spec.ts:171,specs/admin-settings/users.spec.ts:206,specs/admin-settings/users.spec.ts:281
keycloak: true
env:
BASE_URL_OCIS: localhost:9200
Expand Down
7 changes: 4 additions & 3 deletions tests/e2e/environment/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export const test = base.extend<{
globalCleanup: void
globalBeforeHook: void
}>({
world: async ({}, use) => {
const world = new World()
world: async ({}, use, testInfo) => {
const world = new World(testInfo.workerIndex, testInfo.testId)
await use(world)
},
globalCleanup: [
Expand Down Expand Up @@ -68,11 +68,12 @@ export const test = base.extend<{
if (!config.basicAuth && !config.predefinedUsers && !config.mfa) {
let user = world.usersEnvironment.getUser({ key: config.adminUsername })
if (config.keycloak) {
user = world.usersEnvironment.getUser({ key: config.keycloakAdminUser })
const user = world.usersEnvironment.getUser({ key: config.keycloakAdminUser, world })
await api.keycloak.setAccessTokenForKeycloakOcisUser(user)
await api.keycloak.setAccessTokenForKeycloakUser(user)
await storeKeycloakGroups(user, world.usersEnvironment)
} else {
const user = world.usersEnvironment.getUser({ key: config.adminUsername, world })
await api.token.setAccessAndRefreshToken(user)
if (isOcm(testInfo)) {
config.federatedServer = true
Expand Down
58 changes: 57 additions & 1 deletion tests/e2e/environment/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import { environment } from '../support'
import { state } from './shared'

export class World {
workerIndex: number
testId: string
private idCache = new Map<string, string>()

actorsEnvironment: environment.ActorsEnvironment
filesEnvironment: environment.FilesEnvironment
linksEnvironment: environment.LinksEnvironment
spacesEnvironment: environment.SpacesEnvironment
usersEnvironment: environment.UsersEnvironment

constructor() {
constructor(workerIndex: number = 0, testId: string = '') {
this.workerIndex = workerIndex
this.testId = testId

this.usersEnvironment = new environment.UsersEnvironment()
this.spacesEnvironment = new environment.SpacesEnvironment()
this.filesEnvironment = new environment.FilesEnvironment()
Expand All @@ -27,4 +34,53 @@ export class World {
browser: state.browser
})
}

private generateId(base: string): string {
return `${base}-w${this.workerIndex}-${this.testId}`
}

getGroupId(key: string): string {
const cacheKey = `group:${key}`

if (!this.idCache.has(cacheKey)) {
this.idCache.set(cacheKey, this.generateId(key))
}

return this.idCache.get(cacheKey)!
}

getUserId(key: string): string {
const cacheKey = `user:${key}`

if (!this.idCache.has(cacheKey)) {
this.idCache.set(cacheKey, this.generateId(key))
}

return this.idCache.get(cacheKey)!
}

/**
* Transform resource name for parallel test safety.
* Transforms: testfile.txt -> testfile-w1.txt (only when workerIndex > 0)
*/
getResourceId(key: string): string {
if (this.workerIndex === 0) {
return key
}

const cacheKey = `resource:${key}`

if (!this.idCache.has(cacheKey)) {
const parts = key.split('/')
const fileName = parts[parts.length - 1]
const dir = parts.slice(0, -1).join('/')
const newFileName = fileName.includes('.')
? fileName.replace(/(\.[^.]+)$/, `-w${this.workerIndex}$1`)
: `${fileName}-w${this.workerIndex}`
const transformed = dir ? `${dir}/${newFileName}` : newFileName
this.idCache.set(cacheKey, transformed)
}

return this.idCache.get(cacheKey)!
}
}
7 changes: 4 additions & 3 deletions tests/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ export default defineConfig({
testDir: 'specs',

// Run all tests in parallel.
fullyParallel: false,
fullyParallel: true,

// Fail the build on CI if you accidentally left test.only in the source code.
forbidOnly: !!process.env.CI,

// Retry on CI only.
retries: config.retry,

// Opt out of parallel tests on CI.
workers: 1,
// Run tests in parallel - use CI-determined workers or auto-detect locally
// workers: process.env.CI ? 2 : undefined,
workers: undefined,

// Reporter to use
reporter: [
Expand Down
3 changes: 3 additions & 0 deletions tests/e2e/specs/admin-settings/spaces.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { test } from '../../environment/test'
import * as ui from '../../steps/ui/index'
import * as api from '../../steps/api/api'
import os from 'os'

test.describe('spaces management', () => {
test.beforeEach(async ({ world }) => {
Expand Down Expand Up @@ -163,6 +164,8 @@ test.describe('spaces management', () => {
})

test('list members via sidebar', async ({ world }) => {
console.log('CPUs:', os.cpus().length)
console.log('Workers will be:', Math.ceil(os.cpus().length / 2))
await api.usersHaveBeenCreated({
world,
stepUser: 'Admin',
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/specs/smoke/activity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ test.describe('Users can see all activities of the resources and spaces', () =>
},
{
resource: 'sharedFolder',
activity: '%user_alice_displayName% shared sharedFolder with brian'
activity: '%user_alice_displayName% shared sharedFolder with %user_brian_id%'
},
{
resource: 'sharedFolder',
Expand Down Expand Up @@ -225,7 +225,7 @@ test.describe('Users can see all activities of the resources and spaces', () =>
stepUser: 'Brian',
activities: [
'%user_alice_displayName% shared team via link',
'%user_alice_displayName% added brian as member of team',
'%user_alice_displayName% added %user_brian_id% as member of team',
'%user_alice_displayName% added readme.md to .space'
]
})
Expand Down
25 changes: 13 additions & 12 deletions tests/e2e/steps/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export async function usersHaveBeenCreated({
stepUser: string
users: Array<string>
}): Promise<void> {
const admin = world.usersEnvironment.getUser({ key: stepUser })
const admin = world.usersEnvironment.getUser({ key: stepUser, world })
for (const userToBeCreated of users) {
const user = world.usersEnvironment.getUser({ key: userToBeCreated })
const user = world.usersEnvironment.getUser({ key: userToBeCreated, world })
// do not try to create users when using predefined users
if (!config.predefinedUsers) {
await api.provision.createUser({ user, admin })
Expand Down Expand Up @@ -169,7 +169,7 @@ export async function userHasAssignedRolesToUsers({
}) {
const admin = world.usersEnvironment.getUser({ key: stepUser })
for (const { id, role } of users) {
const user = world.usersEnvironment.getUser({ key: id })
const user = world.usersEnvironment.getUser({ key: id, world })
/**
The oCIS API request for assigning roles allows only one role per user,
whereas the Keycloak API request can assign multiple roles to a user.
Expand Down Expand Up @@ -200,7 +200,8 @@ export async function userHasCreatedProjectSpaces({
})
world.spacesEnvironment.createSpace({
key: space.id || space.name,
space: { name: space.name, id: spaceId }
space: { name: space.name, id: spaceId },
world
})
}
}
Expand Down Expand Up @@ -276,11 +277,11 @@ export async function usersHaveBeenAddedToGroup({
stepUser: string
usersToAdd: { user: string; group: string }[]
}) {
const admin = world.usersEnvironment.getUser({ key: stepUser })
const admin = world.usersEnvironment.getUser({ key: stepUser, world })
for (const info of usersToAdd) {
const group = world.usersEnvironment.getGroup({ key: info.group })
const user = world.usersEnvironment.getUser({ key: info.user })
await api.graph.addUserToGroup({ user, group, admin })
const group = world.usersEnvironment.getGroup({ key: info.group, world })
const user = world.usersEnvironment.getUser({ key: info.user, world })
await api.graph.addUserToGroup({ user, group, admin, world })
}
}

Expand All @@ -294,8 +295,8 @@ export async function userHasDeletedGroup({
name: string
}): Promise<void> {
const admin = world.usersEnvironment.getUser({ key: stepUser })
const group = world.usersEnvironment.getGroup({ key: name })
await api.graph.deleteGroup({ group, admin })
const group = world.usersEnvironment.getGroup({ key: name, world })
await api.graph.deleteGroup({ group, admin, world })
}

export async function userHasAddedMembersToSpace({
Expand Down Expand Up @@ -330,9 +331,9 @@ export async function groupsHaveBeenCreated({
groupIds: string[]
stepUser: string
}): Promise<void> {
const admin = world.usersEnvironment.getUser({ key: stepUser })
const admin = world.usersEnvironment.getUser({ key: stepUser, world })
for (const groupId of groupIds) {
const group = world.usersEnvironment.getGroup({ key: groupId })
const group = world.usersEnvironment.getGroup({ key: groupId, world })
await api.graph.createGroup({ group, admin })
}
}
Expand Down
12 changes: 6 additions & 6 deletions tests/e2e/steps/ui/adminSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ export async function userCreatesGroups({
groupIds: string[]
}): Promise<void> {
const { page } = world.actorsEnvironment.getActor({ key: stepUser })
const groupsObject = new objects.applicationAdminSettings.Groups({ page })
const groupsObject = new objects.applicationAdminSettings.Groups({ page, world })
for (const groupId of groupIds) {
await groupsObject.createGroup({ key: groupId })
}
Expand All @@ -500,7 +500,7 @@ export async function userShouldSeeGroupIds({
expectedGroupIds: string[]
}): Promise<void> {
const { page } = world.actorsEnvironment.getActor({ key: stepUser })
const groupsObject = new objects.applicationAdminSettings.Groups({ page })
const groupsObject = new objects.applicationAdminSettings.Groups({ page, world })
const actualGroupsIds = await groupsObject.getDisplayedGroupsIds()
for (const group of expectedGroupIds) {
expect(actualGroupsIds).toContain(groupsObject.getUUID({ key: group }))
Expand All @@ -517,7 +517,7 @@ export async function userShouldNotSeeGroupIds({
expectedGroupIds: string[]
}): Promise<void> {
const { page } = world.actorsEnvironment.getActor({ key: stepUser })
const groupsObject = new objects.applicationAdminSettings.Groups({ page })
const groupsObject = new objects.applicationAdminSettings.Groups({ page, world })
const actualGroupsIds = await groupsObject.getDisplayedGroupsIds()
for (const group of expectedGroupIds) {
expect(actualGroupsIds).not.toContain(groupsObject.getUUID({ key: group }))
Expand All @@ -534,7 +534,7 @@ export async function userShouldSeeGroupDisplayName({
groupDisplayName: string
}): Promise<void> {
const { page } = world.actorsEnvironment.getActor({ key: stepUser })
const groupsObject = new objects.applicationAdminSettings.Groups({ page })
const groupsObject = new objects.applicationAdminSettings.Groups({ page, world })
const groups = await groupsObject.getGroupsDisplayName()
expect(groups).toContain(groupDisplayName)
}
Expand All @@ -551,7 +551,7 @@ export async function userDeletesGroups({
groupsToBeDeleted: string[]
}): Promise<void> {
const { page } = world.actorsEnvironment.getActor({ key: stepUser })
const groupsObject = new objects.applicationAdminSettings.Groups({ page })
const groupsObject = new objects.applicationAdminSettings.Groups({ page, world })
const groupIds = []
switch (actionType) {
case fileAction.batchAction:
Expand Down Expand Up @@ -587,7 +587,7 @@ export async function userChangesGroup({
action: typeof fileAction.contextMenu | typeof fileAction.quickAction
}): Promise<void> {
const { page } = world.actorsEnvironment.getActor({ key: stepUser })
const groupsObject = new objects.applicationAdminSettings.Groups({ page })
const groupsObject = new objects.applicationAdminSettings.Groups({ page, world })
await groupsObject.changeGroup({
key,
attribute: attribute,
Expand Down
Loading
Loading