Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(e2e): migrate unlinked page tests to playwright #1790

Merged
merged 3 commits into from
Mar 15, 2024
Merged
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
214 changes: 0 additions & 214 deletions cypress/e2e/editPage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,220 +23,6 @@ describe("editPage.spec", () => {
cy.setGithubSessionDefaults()
})

describe("Edit unlinked page", () => {
const TEST_PAGE_CONTENT = "lorem ipsum"
const TEST_INSTAGRAM_EMBED_SCRIPT =
'<script async src="//www.instagram.com/embed.js"></script>'
const TEST_SANITIZED_INSTAGRAM_EMBED_SCRIPT =
'<script async="" src="//www.instagram.com/embed.js"></script>'
const TEST_UNTRUSTED_SCRIPT =
'<script src="https://www.example.com/evil.js"></script>'
const TEST_INLINE_SCRIPT = '<script>alert("hello")</script>'

const TEST_UNLINKED_PAGE_TITLE = "Test Unlinked Page"
const TEST_UNLINKED_PAGE_FILENAME = titleToPageFileName(
TEST_UNLINKED_PAGE_TITLE
)
const TEST_PAGE_TITLE_ENCODED = encodeURIComponent(
TEST_UNLINKED_PAGE_FILENAME
)

const DEFAULT_IMAGE_TITLE = "isomer-logo.svg"
const ADDED_IMAGE_TITLE = "balloon"
const ADDED_IMAGE_PATH = "images/balloon.png"

const ADDED_FILE_TITLE = "singapore-pages"
const ADDED_FILE_PATH = "files/singapore.pdf"

const LINK_TITLE = "link"
const LINK_URL = "https://www.google.com"

before(() => {
cy.setDefaultSettings()

// NOTE: We need to repeat the interceptor here as
// cypress resolves by type before nesting level.
// Hence, the alias here will not be resolved as the `before` hook
// will be resolved before the outer `beforeEach`
cy.setupDefaultInterceptors()

// Set up test resource categories
cy.visit(`/sites/${TEST_REPO_NAME}/workspace`)
cy.contains("a", "Create page").click({ force: true })
cy.get("#title").clear().type(TEST_UNLINKED_PAGE_TITLE)
cy.contains("Save").click().wait(Interceptors.POST)
})

beforeEach(() => {
cy.visit(`/sites/${TEST_REPO_NAME}/editPage/${TEST_PAGE_TITLE_ENCODED}`)
cy.contains("verify").should("not.exist")
})

it("Edit page (unlinked) should have correct colour", () => {
cy.get("#display-header").should(
"have.css",
"background-color",
PRIMARY_COLOUR
)
})

it("Edit page (unlinked) should have name of title", () => {
cy.contains(TEST_UNLINKED_PAGE_TITLE)
})

it("Edit page (unlinked) should provide a warning to users when navigating away", () => {
cy.get(".CodeMirror-scroll").type(TEST_PAGE_CONTENT)
cy.get('button[aria-label="Back to sites"]').click()

cy.contains("Warning")
cy.contains(":button", "No").click()

// Sanity check: still in unlinked pages and content still present
cy.url().should(
"include",
`${CMS_BASEURL}/sites/${TEST_REPO_NAME}/editPage/${TEST_PAGE_TITLE_ENCODED}`
)
cy.contains(TEST_PAGE_CONTENT)

cy.get('button[aria-label="Back to sites"]').click()

cy.contains("Warning")
cy.contains(":button", "Yes").click()

// Assert: in Workspace
cy.url().should(
"include",
`${CMS_BASEURL}/sites/${TEST_REPO_NAME}/workspace`
)
})

it("Edit page (unlinked) should allow user to modify and save content", () => {
cy.get(".CodeMirror-scroll").type(TEST_PAGE_CONTENT)
cy.contains(":button", "Save").click()

// Asserts
// 1. Toast
cy.contains(SUCCESSFUL_EDIT_PAGE_TOAST)

// 2. Content is there even after refreshing
cy.reload()
cy.contains(TEST_PAGE_CONTENT).should("exist")
})

it("Edit page (unlinked) should allow user to add existing image", () => {
// NOTE: Multiple GET requests are fired off and hence, unable to use default GET to match
cy.intercept("**/images").as("getImages")
cy.get(".image").click().wait("@getImages")
cy.contains(DEFAULT_IMAGE_TITLE).should("exist").click()
cy.contains(":button", "Select").click()

cy.get("#altText").clear().type("Hello World")
cy.contains(":button", "Save").click()

cy.contains(`/images/${DEFAULT_IMAGE_TITLE}`)
})

it("Edit page (unlinked) should allow user to upload and add new image", () => {
cy.get(".image").click().wait(Interceptors.GET)
cy.contains(":button", "Add new").click()

cy.get("#file-upload").attachFile(ADDED_IMAGE_PATH)
cy.get("#name").clear().type(ADDED_IMAGE_TITLE)
cy.get("button")
.contains(/^Upload$/)
.click()
.wait(Interceptors.POST)

cy.get("#altText").clear().type("Hello World")
cy.contains(":button", "Save").click()

cy.contains(`/images/${ADDED_IMAGE_TITLE}`)
})

it("Edit page (unlinked) should allow user to upload and add new file", () => {
cy.get(".file").click().wait(Interceptors.GET)
cy.contains(":button", "Add new").click()

cy.get("#file-upload").attachFile(ADDED_FILE_PATH)
cy.get("#name").clear().type(ADDED_FILE_TITLE)
cy.get("button")
.contains(/^Upload$/)
.click()
.wait(Interceptors.POST)

cy.get("#altText").clear().type("Hello World")
cy.contains(":button", "Save").click()

cy.contains(`/files/${ADDED_FILE_TITLE}`)
})

it("Edit page (unlinked) should allow user to add existing file", () => {
cy.get(".file").click().wait(Interceptors.GET)
cy.contains(ADDED_FILE_TITLE).click()
cy.contains(":button", "Select").click()

cy.get("#altText").clear().type("Hello World")
cy.contains(":button", "Save").click()
cy.contains(`/files/${ADDED_FILE_TITLE}`)
})

it("Edit page (unlinked) should allow user to add link", () => {
cy.get(".link").click()

cy.get('input[id="text"]').type(LINK_TITLE)
cy.get('input[id="link"]').type(LINK_URL)
cy.contains(":button", "Save").click()

cy.contains(`[${LINK_TITLE}](${LINK_URL})`)
})

it("Edit page (unlinked) should allow users to add Instagram embed script", () => {
cy.get(".CodeMirror-scroll").type(TEST_INSTAGRAM_EMBED_SCRIPT)
cy.contains(":button", "Save").click()

// Asserts
// 1. Toast
cy.contains(SUCCESSFUL_EDIT_PAGE_TOAST)

// 2. Content is there even after refreshing
cy.reload()
cy.contains(TEST_SANITIZED_INSTAGRAM_EMBED_SCRIPT).should("exist")
})

it("Edit page (unlinked) should not allow users to add untrusted external scripts", () => {
cy.get(".CodeMirror-scroll").type(TEST_UNTRUSTED_SCRIPT)

// Asserts
// 1. Save button is disabled
cy.contains(":button", "Save").should("be.disabled")

// 2. CSP warning appears
cy.contains(
"Intended <script> content violates Content Security Policy and therefore could not be displayed. Isomer does not support display of any forbidden resources."
).should("exist")

// 3. Content is not saved
cy.reload()
cy.contains(TEST_UNTRUSTED_SCRIPT).should("not.exist")
})

it("Edit page (unlinked) should not allow users to add inline scripts", () => {
cy.get(".CodeMirror-scroll").type(TEST_INLINE_SCRIPT)
cy.contains(":button", "Save").click()

// Asserts
// 1. XSS warning modal is shown
cy.contains(
"There is unauthorised JS detected in the following snippet"
).should("exist")

// 2. Content is not saved
cy.contains(":button", "Acknowledge").click()
cy.reload()
cy.contains(TEST_INLINE_SCRIPT).should("not.exist")
})
})

describe("Edit collection page", () => {
const TEST_FOLDER_TITLE = "Test Edit Collection Category"
const TEST_FOLDER_TITLE_SLUGIFIED = slugifyCategory(TEST_FOLDER_TITLE)
Expand Down
12 changes: 12 additions & 0 deletions e2e/api/context.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { APIRequestContext, request } from "@playwright/test"

export const getApi = async (
storageState: Parameters<typeof request.newContext>[0]["storageState"]
): Promise<APIRequestContext> => {
const req = await request.newContext({
baseURL: process.env.CYPRESS_BACKEND_URL,
storageState,
})

return req
}
Loading
Loading