How to Test a SharePoint Site Page with Cypress
Build reliable Cypress tests for SharePoint pages with stable selectors, programmatic auth, API checks, synchronization, and troubleshooting guidance.

Direct answer: Use Cypress browser tests to verify what a SharePoint user can see and do: the page loads, the expected heading or web part appears, links point to the right destination, and controls produce the intended result. Authenticate through your tenant’s approved test flow, use stable selectors, and let Cypress retry queries and assertions instead of adding arbitrary sleeps. Add cy.request() checks when you need to validate an HTTP contract or prepare test data; an API check alone cannot prove that a SharePoint page rendered correctly.
1. Define the behavior before writing the test
Start with the exact page URL, tenant, page type, user role, and user-visible outcome you want to protect. Examples include:
- A page heading is visible and has the expected text.
- A news, navigation, or call-to-action link goes to the intended destination.
- A custom web part renders data for the signed-in role.
- An announcement, error state, or empty state appears when its condition is met.
- A control opens a panel, submits a form, or changes the page state.
Keep a dedicated non-production site and test account where possible. Grant the account only the permissions required by the scenario, and follow your organization’s Microsoft 365 identity policies.
2. Install and configure Cypress
In an existing JavaScript project:
npm install --save-dev cypress
npx cypress open
For headless CI runs, Cypress can execute the same tests with:
npx cypress run --browser chrome
Set the site URL in an environment variable or Cypress configuration rather than hard-coding a tenant address in every test:
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: process.env.SHAREPOINT_BASE_URL || 'https://contoso.sharepoint.com',
video: false,
},
})
Use your CI system’s secret store for credentials and tokens. Cypress recommends keeping secrets out of test files and controlling test state programmatically. See the Cypress best practices documentation.
3. Authenticate without coupling tests to the login screen
Microsoft 365 authentication varies by tenant, conditional access, MFA, identity provider, and test environment. There is no universal login recipe that is safe for every SharePoint tenant. Prefer a programmatic setup that your organization permits, then cache the resulting browser state with cy.session().
The following shape is intentionally environment-specific. Replace the setup with your approved token, cookie, or identity-provider flow; do not place real secrets in the repository.
// cypress/support/commands.js
Cypress.Commands.add('loginToSharePoint', () => {
cy.session('sharepoint-test-user', () => {
// Implement the tenant-approved programmatic login here.
// For example, obtain a test session through an internal helper
// and set the cookies or storage values it returns.
cy.visit('/sites/example')
})
})
// cypress/e2e/sharepoint-page.cy.js
beforeEach(() => {
cy.loginToSharePoint()
})
cy.session() captures and restores browser context such as cookies and web storage, which avoids repeating an expensive login for every test. Validate the session by visiting a page that requires authentication and asserting a signed-in-only element.
4. Write a user-visible page test
If the page or custom component provides a stable data-* hook, use it. Cypress recommends these attributes because they are less coupled to CSS and JavaScript implementation details.

describe('SharePoint overview page', () => {
beforeEach(() => {
cy.loginToSharePoint()
})
it('shows the expected page content', () => {
cy.visit('/sites/example/SitePages/overview.aspx')
cy.get('[data-cy="page-heading"]')
.should('be.visible')
.and('have.text', 'Overview')
cy.get('[data-cy="contact-link"]')
.should('be.visible')
.and('have.attr', 'href')
.and('include', '/contact')
})
})
data-cy is illustrative. SharePoint-managed markup may not expose test-specific attributes, and you may not be able to add them to Microsoft-owned page structure. In that case, choose the most stable semantic locator available, such as an accessible role and name or a heading, and document that a SharePoint markup change may require maintenance.
5. Synchronize with page state, not fixed sleeps
SharePoint pages often load web parts and data asynchronously. Cypress queries and assertions retry until they pass or the command timeout expires. Assert the state you need instead of using cy.wait(5000) as a routine workaround.
it('waits for an asynchronously rendered web part', () => {
cy.visit('/sites/example/SitePages/overview.aspx')
cy.get('[data-cy="results"]')
.should('be.visible')
.and('not.be.empty')
})
When a particular request defines readiness, intercept that request and wait on its alias:
it('waits for the list data before asserting rows', () => {
cy.intercept('GET', '**/_api/web/lists/**/items*').as('listItems')
cy.visit('/sites/example/SitePages/overview.aspx')
cy.wait('@listItems').its('response.statusCode').should('eq', 200)
cy.get('[data-cy="list-row"]').should('have.length.at.least', 1)
})
Use a specific request pattern that matches the page’s contract. Overly broad intercepts can wait on the wrong call or make unrelated changes break the test. Cypress documents request interception and retrying assertions in its network request guidance and retry-ability documentation.
6. Test controls and navigation as a user would
Keep assertions focused on outcomes rather than internal implementation. For a button that opens a panel:
it('opens the details panel', () => {
cy.visit('/sites/example/SitePages/overview.aspx')
cy.get('[data-cy="details-button"]').click()
cy.get('[role="dialog"]').should('be.visible')
cy.get('[role="dialog"]').should('contain.text', 'Details')
})
For a link, assert its destination or follow it when navigation itself is part of the requirement:
it('takes the user to the policy page', () => {
cy.visit('/sites/example/SitePages/overview.aspx')
cy.get('[data-cy="policy-link"]').click()
cy.location('pathname').should('include', '/SitePages/policy.aspx')
})
Do not assert every generated class, layout wrapper, or internal SharePoint component. Those details create brittle tests without proving a user-visible requirement.
7. Add API checks for data and setup questions
Cypress can send HTTP requests without opening a browser. This is useful for checking an endpoint’s status, headers, and response body, or for creating test data before a UI test.

it('returns the expected list item through SharePoint REST', () => {
cy.request({
method: 'GET',
url: '/sites/example/_api/web/lists/getbytitle(\'Announcements\')/items',
headers: { Accept: 'application/json;odata=nometadata' },
}).then((response) => {
expect(response.status).to.eq(200)
expect(response.headers).to.have.property('content-type')
expect(response.body.value).to.be.an('array')
})
})
SharePoint REST resources use the site’s /_api path, with entry points such as /_api/site and /_api/web. Microsoft documents how to construct these endpoints by walking the SharePoint object model in Complete basic operations using SharePoint REST endpoints and Get to know the SharePoint REST service.
Microsoft’s SharePoint REST v2 guidance says new REST innovation is driven through Microsoft Graph. Native SharePoint REST can still fit an application that already has SharePoint access tokens. Do not assume a token issued for Graph automatically authorizes a native SharePoint REST request; confirm the audience and permissions for your environment. See Microsoft’s SharePoint sites and content API overview and REST v2 operations using Microsoft Graph.
An API test proves an HTTP contract or data condition. It does not prove that a browser rendered the page, applied permissions, loaded a web part, or exposed an accessible control. Keep the browser and API checks as separate tests when they answer separate questions.
8. SharePoint-specific cases to account for
Uploaded HTML pages
Microsoft’s documented uploaded-HTML-page feature has special constraints: the maximum upload is 10 MB, the page is rendered in a sandboxed iframe isolated from SharePoint navigation, and SharePoint blocks arbitrary fetch requests and outbound API calls from those HTML pages. These rules apply to that HTML-page feature; do not generalize them to every modern SharePoint page. Test the actual page type used by your site. See Microsoft’s HTML page guidance.
Diagnostics versus behavior tests
Microsoft’s Page Diagnostics for SharePoint browser extension helps investigate performance on modern team, communication, and hub sites. It complements Cypress: diagnostics can explain a slow or unhealthy page, while Cypress verifies the behavior your users must receive.
Legacy extensions
Microsoft says the SharePoint Add-In model in SharePoint Online was deprecated on November 27, 2023 and fully retired on April 2, 2026. Review old fixtures and extensions that depend on that model; Microsoft recommends SharePoint Framework for replacement work.
9. A maintainable test structure
Keep page-specific selectors and actions in small helpers, while leaving business assertions in the spec:
// cypress/support/pages/overview.js
export const overviewPage = {
visit() {
cy.visit('/sites/example/SitePages/overview.aspx')
},
heading() {
return cy.get('[data-cy="page-heading"]')
},
contactLink() {
return cy.get('[data-cy="contact-link"]')
},
}
// cypress/e2e/overview.cy.js
import { overviewPage } from '../support/pages/overview'
describe('overview page', () => {
beforeEach(() => cy.loginToSharePoint())
it('shows the page heading', () => {
overviewPage.visit()
overviewPage.heading().should('be.visible').and('contain.text', 'Overview')
})
})
When a selector changes, update one helper rather than searching through every assertion. Keep test data deterministic and reset it through an approved API or fixture setup.
10. Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Redirected to a Microsoft login page | The session was not created, expired, or was blocked by tenant policy. | Verify the programmatic login flow, required permissions, and conditional-access rules. Assert the authenticated state before testing the page. |
cy.get() times out |
The selector is wrong, the web part is still loading, or the page never rendered the element. | Inspect the DOM, choose a stable semantic or data-* selector, and assert the actual ready state. Intercept and wait for the specific data request if needed. |
| Test passes locally but fails in CI | Different browser, viewport, account, tenant data, or network conditions. | Run the same browser and role in CI, remove shared mutable data, and capture the failure artifacts. Do not hide the difference with a longer fixed sleep. |
| Expected text is empty | The assertion ran before asynchronous content arrived, or the text is in an iframe or different component. | Wait for the relevant request or visible state, then assert. Confirm whether the page type uses an iframe and whether the content is available to Cypress. |
cy.request() returns 401 or 403 |
The request lacks a valid token, has the wrong audience, or lacks SharePoint permissions. | Use the authentication method approved for the tenant and verify Graph versus native SharePoint REST token requirements. |
| Element is covered or not actionable | A consent dialog, overlay, sticky header, or loading layer is intercepting the click. | Assert that the overlay is gone or close it through the user-visible control, then click. Avoid forcing clicks unless covering behavior is the thing being tested. |
| Selectors break after SharePoint changes | The test depends on generated classes or internal DOM structure. | Move to a supplied data-* hook, accessible role/name, heading, or another stable semantic locator. |
11. Performance, reliability, and cost
- Use the right layer: API setup and contract checks are usually faster than loading a full browser page. Reserve end-to-end tests for rendered behavior and user workflows.
- Reuse sessions:
cy.session()reduces repeated authentication work while preserving a clean, explicit session boundary. - Keep tests independent: Seed only the data a test needs and avoid ordering dependencies. A dedicated test site and account reduce surprises from production edits.
- Wait on evidence: Assertions and aliased requests adapt to real response time better than fixed delays, which either waste time or remain too short.
- Control scope: A small set of critical browser tests gives clearer failures than duplicating every API assertion through the UI.
- Expect tenant variance: Browser, identity, permission, and SharePoint page behavior can differ by organization. Validate the exact tenant and role used in deployment.
Cypress itself has no per-screenshot or per-request charge described by the sources for this guide. Your costs come from the CI runners, test environment, and any separate services your organization uses.
12. Or skip the browser setup
If your goal is to capture a SharePoint page for a visual record, review, or downstream workflow rather than interact with it, ScreenshotNeo provides a one-request website screenshot API. Its capture options include custom headers, cookies, user agents, authorization, waits, full-page capture, and PDF output, so an authorized SharePoint URL can be handled without maintaining a browser harness.
See the ScreenshotNeo API documentation for the full parameter list.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://contoso.sharepoint.com/sites/example/SitePages/overview.aspx -o sharepoint.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://contoso.sharepoint.com/sites/example/SitePages/overview.aspx",
},
timeout=90,
)
r.raise_for_status()
open("sharepoint.webp", "wb").write(r.content)
const fs = require('node:fs/promises');
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://contoso.sharepoint.com/sites/example/SitePages/overview.aspx',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
await fs.writeFile('sharepoint.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
13. FAQ
Can Cypress test a SharePoint site?
Yes. Cypress can drive a browser against SharePoint and assert rendered content, navigation, controls, and role-specific outcomes. Authentication and selectors must match your tenant and page implementation.
Should every SharePoint test use the UI?
No. Use browser tests for user-visible behavior and direct API tests for HTTP contracts, data setup, and data validation. Keep both when they prove different things.
Is cy.wait(5000) ever appropriate?
A fixed wait can be a temporary diagnostic, but it is a poor default. Prefer a retried assertion on the expected state or cy.wait() on a specifically intercepted request.
What if I cannot add data-cy attributes?
Use stable accessible roles, names, headings, labels, or carefully chosen text and document the maintenance risk of SharePoint-managed markup.
Can a screenshot replace a Cypress test?
No. A screenshot records visual output. It does not verify interactions, permissions, network contracts, or whether a control behaves correctly. Use it as a separate capture or review step.


