Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion .drone.star
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,10 @@ def stagePipelines(ctx):
if (determineReleasePackage(ctx) != None):
return unit_test_pipelines

e2e_playwright_pipeline = e2eTestsOnPlaywright(ctx)
e2e_pipelines = e2eTests(ctx)
keycloak_pipelines = e2eTestsOnKeycloak(ctx)
return unit_test_pipelines + pipelinesDependsOn(e2e_pipelines + keycloak_pipelines, unit_test_pipelines)
return unit_test_pipelines + pipelinesDependsOn(e2e_playwright_pipeline + e2e_pipelines + keycloak_pipelines, unit_test_pipelines)

def afterPipelines(ctx):
return build(ctx) + pipelinesDependsOn(notify(ctx), build(ctx))
Expand Down Expand Up @@ -535,6 +536,64 @@ def unitTests(ctx):
},
}]

def e2eTestsOnPlaywright(ctx):
e2e_workspace = {
"base": dir["base"],
"path": config["app"],
}

e2e_trigger = {
"ref": [
"refs/heads/master",
"refs/heads/stable-*",
"refs/tags/**",
"refs/pull/**",
],
}

pipelines = []

# pipeline steps
steps = skipIfUnchanged(ctx, "e2e-tests-playwright")

environment = {
"BASE_URL_OCIS": "ocis:9200",
"PLAYWRIGHT_BROWSERS_PATH": ".playwright",
}

steps += restoreBuildArtifactCache(ctx, "pnpm", ".pnpm-store") + \
installPnpm() + \
restoreBrowsersCache() + \
restoreBuildArtifactCache(ctx, "web-dist", "dist")

if ctx.build.event == "cron":
steps += restoreBuildArtifactCache(ctx, "ocis", "ocis")
else:
steps += restoreOcisCache()

steps += ocisService()

steps += [{
"name": "e2e-tests-playwright",
"image": OC_CI_NODEJS_IMAGE,
"environment": environment,
"commands": [
"pnpm test:e2e:playwright --project=chromium",
],
}]

pipelines.append({
"kind": "pipeline",
"type": "docker",
"name": "e2e-tests-playwright",
"workspace": e2e_workspace,
"steps": steps,
"depends_on": ["cache-ocis"],
"trigger": e2e_trigger,
})

return pipelines

def e2eTests(ctx):
e2e_workspace = {
"base": dir["base"],
Expand Down Expand Up @@ -1365,12 +1424,26 @@ def skipIfUnchanged(ctx, type):
}
return [skip_step]

if type == "e2e-tests-playwright":
e2e_playwright_skip_steps = [
"^__fixtures__/.*",
"^__mocks__/.*",
"^packages/.*/tests/.*",
"^tests/unit/.*",
"^tests/e2e/.*",
]
skip_step["settings"] = {
"ALLOW_SKIP_CHANGED": base_skip_steps + e2e_playwright_skip_steps,
}
return [skip_step]

if type == "e2e-tests":
e2e_skip_steps = [
"^__fixtures__/.*",
"^__mocks__/.*",
"^packages/.*/tests/.*",
"^tests/unit/.*",
"^tests/e2e-playwright/.*",
]
skip_step["settings"] = {
"ALLOW_SKIP_CHANGED": base_skip_steps + e2e_skip_steps,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"lint": "eslint vite.config.ts '{packages,tests}/**/*.{js,ts,vue}' --color",
"serve": "SERVER=true pnpm build:w",
"test:e2e:cucumber": "NODE_TLS_REJECT_UNAUTHORIZED=0 TS_NODE_PROJECT=./tests/e2e/cucumber/tsconfig.json cucumber-js --profile=e2e -f json:tests/e2e/cucumber/report/cucumber_report.json",
"test:e2e:playwright": "NODE_TLS_REJECT_UNAUTHORIZED=0 npx playwright test --config=tests/e2e-playwright/",
"test:unit": "NODE_OPTIONS=--unhandled-rejections=throw vitest",
"licenses:check": "license-checker-rseidelsohn --summary --relativeLicensePath --onlyAllow 'Python-2.0;Apache*;Apache License, Version 2.0;Apache-2.0;Apache 2.0;Artistic-2.0;BSD;BSD-3-Clause;CC-BY-3.0;CC-BY-4.0;CC0-1.0;ISC;MIT;MPL-2.0;Public Domain;Unicode-TOU;Unlicense;WTFPL;BlueOak-1.0.0' --excludePackages '@ownclouders/babel-preset;@ownclouders/eslint-config;@ownclouders/prettier-config;@ownclouders/tsconfig;@ownclouders/web-client;@ownclouders/web-pkg;external;web-app-files;text-editor;preview;web-app-ocm;@ownclouders/design-system;pdf-viewer;web-app-search;admin-settings;webfinger;web-runtime;@ownclouders/web-test-helpers'",
"licenses:csv": "license-checker-rseidelsohn --relativeLicensePath --csv --out ./third-party-licenses/third-party-licenses.csv",
Expand Down
10 changes: 10 additions & 0 deletions tests/e2e-playwright/helpers/setAccessAndRefreshToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { config } from '../../e2e/config.js'
import { api } from '../../e2e/support'
import { UsersEnvironment } from '../../e2e/support/environment'

export async function setAccessAndRefreshToken(usersEnvironment: UsersEnvironment) {
if (!config.basicAuth && !config.predefinedUsers) {
let user = usersEnvironment.getUser({ key: config.adminUsername })
await api.token.setAccessAndRefreshToken(user)
}
}
46 changes: 46 additions & 0 deletions tests/e2e-playwright/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { defineConfig, devices } from '@playwright/test'

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
// Look for test files in the "tests" directory, relative to this configuration file.
testDir: 'specs',

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

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

// Retry on CI only.
retries: process.env.CI ? 1 : 0,

// Opt out of parallel tests on CI.
workers: process.env.CI ? 1 : undefined,

// Reporter to use
reporter: 'html',

use: {
ignoreHTTPSErrors: true,

// Collect trace when retrying the failed test.
trace: 'on-first-retry'
},
// Configure projects for major browsers.
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] }
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] }
}
]
})
88 changes: 88 additions & 0 deletions tests/e2e-playwright/specs/internalLink.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { expect, test } from '@playwright/test'
import { config } from '../../e2e/config.js'
import {
ActorsEnvironment,
UsersEnvironment,
LinksEnvironment,
FilesEnvironment
} from '../../e2e/support/environment'
import {
userHasBeenCreated,
userHasCreatedFolder,
userHasSharedResource,
userHasCreatedPublicLinkOfResource
} from '../steps/api/api'
import { setAccessAndRefreshToken } from '../helpers/setAccessAndRefreshToken'
import { openPublicLink } from '../steps/ui/public'
import { navigateToSharedWithMePage, updateShareeRole } from '../steps/ui/shares'
import { uploadResource, isAbleToEditFileOrFolder } from '../steps/ui/resources'
import { LogInUser, LogOutUser } from '../steps/ui/session'

test.describe('internal link share', () => {
let actorsEnvironment
const usersEnvironment = new UsersEnvironment()
const linksEnvironment = new LinksEnvironment()
const filesEnvironment = new FilesEnvironment()

test.beforeEach(async ({ browser }) => {
actorsEnvironment = new ActorsEnvironment({
context: {
acceptDownloads: config.acceptDownloads,
reportDir: config.reportDir,
tracingReportDir: config.tracingReportDir,
reportHar: config.reportHar,
reportTracing: config.reportTracing,
reportVideo: config.reportVideo,
failOnUncaughtConsoleError: config.failOnUncaughtConsoleError
},
browser: browser
})

await setAccessAndRefreshToken(usersEnvironment)

await userHasBeenCreated(usersEnvironment, 'Admin', 'Alice')
await userHasBeenCreated(usersEnvironment, 'Admin', 'Brian')

await LogInUser(usersEnvironment, actorsEnvironment, 'Alice')
await LogInUser(usersEnvironment, actorsEnvironment, 'Brian')

await userHasCreatedFolder(usersEnvironment, 'Alice', 'myfolder')

await userHasSharedResource(
usersEnvironment,
'Alice',
'myfolder',
'Brian',
'user',
'Can edit',
'folder'
)

await userHasCreatedPublicLinkOfResource(
usersEnvironment,
'Alice',
'myfolder',
'Invited people'
)
})

test('opening a link with internal role', async () => {
await openPublicLink(actorsEnvironment, linksEnvironment, 'Brian', 'Unnamed link')
await navigateToSharedWithMePage(actorsEnvironment, 'Brian')
await uploadResource(actorsEnvironment, filesEnvironment, 'Brian', 'simple.pdf', 'myfolder')
await updateShareeRole(
usersEnvironment,
actorsEnvironment,
'Alice',
'myfolder',
'Brian',
'user',
'Can view',
'folder'
)
await LogOutUser(actorsEnvironment, 'Alice')

expect(await isAbleToEditFileOrFolder(actorsEnvironment, 'Brian', 'myfolder')).toBeFalsy()
await LogOutUser(actorsEnvironment, 'Brian')
})
})
67 changes: 67 additions & 0 deletions tests/e2e-playwright/steps/api/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { config } from '../../../e2e/config.js'
import { UsersEnvironment } from '../../../e2e/support/environment'
import { api } from '../../../e2e/support'
import { ResourceType } from '../../../e2e/support/api/share/share'

export async function userHasBeenCreated(
usersEnvironment: UsersEnvironment,
stepUser: string,
userToBeCreated: string
): Promise<void> {
const admin = usersEnvironment.getUser({ key: stepUser })
const user = usersEnvironment.getUser({ key: userToBeCreated })
// do not try to create users when using predefined users
if (!config.predefinedUsers) {
await api.provision.createUser({ user, admin })
}
}

export async function userHasCreatedFolder(
usersEnvironment: UsersEnvironment,
stepUser: string,
folderName: string
): Promise<void> {
const user = usersEnvironment.getUser({ key: stepUser })
await api.dav.createFolderInsidePersonalSpace({ user, folder: folderName })
}

export async function userHasSharedResource(
usersEnvironment: UsersEnvironment,
stepUser: string,
resource: string,
recipient: string,
type: string,
role: string,
resourceType: ResourceType
): Promise<void> {
const user = usersEnvironment.getUser({ key: stepUser })
await api.share.createShare({
user,
path: resource,
shareType: type,
shareWith: recipient,
role: role,
resourceType: resourceType as ResourceType
})
}

export async function userHasCreatedPublicLinkOfResource(
usersEnvironment: UsersEnvironment,
stepUser: string,
resource: string,
role: string,
name?: string,
password?: undefined,
space?: 'Personal'
) {
const user = usersEnvironment.getUser({ key: stepUser })

await api.share.createLinkShare({
user,
path: resource,
password: password,
name: name ? name : 'Unnamed link',
role: role,
spaceName: space
})
}
18 changes: 18 additions & 0 deletions tests/e2e-playwright/steps/ui/public.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { objects } from '../../../e2e/support'
import { ActorsEnvironment, LinksEnvironment } from '../../../e2e/support/environment'

export async function openPublicLink(
actorsEnvironment: ActorsEnvironment,
linksEnvironment: LinksEnvironment,
stepUser: string,
name: string
): Promise<void> {
const { page } = await actorsEnvironment.createActor({
key: stepUser,
namespace: actorsEnvironment.generateNamespace(stepUser, stepUser)
})

const { url } = linksEnvironment.getLink({ name })
const pageObject = new objects.applicationFiles.page.Public({ page })
await pageObject.open({ url })
}
32 changes: 32 additions & 0 deletions tests/e2e-playwright/steps/ui/resources.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { objects } from '../../../e2e/support'
import { ActorsEnvironment, FilesEnvironment } from '../../../e2e/support/environment'

export async function uploadResource(
actorsEnvironment: ActorsEnvironment,
filesEnvironment: FilesEnvironment,
stepUser: string,
resource: string,
to: string,
type?: string,
option?: string
): Promise<void> {
const { page } = actorsEnvironment.getActor({ key: stepUser })
const resourceObject = new objects.applicationFiles.Resource({ page })
await resourceObject.upload({
to: to,
resources: [filesEnvironment.getFile({ name: resource })],
option: option,
type: type
})
}

export async function isAbleToEditFileOrFolder(
actorsEnvironment: ActorsEnvironment,
stepUser: string,
resource: string
): Promise<boolean> {
const { page } = actorsEnvironment.getActor({ key: stepUser })
const resourceObject = new objects.applicationFiles.Resource({ page })
const userCanEdit = await resourceObject.canManageResource({ resource })
return userCanEdit
}
Loading