Skip to content

Commit b91535d

Browse files
authored
Merge pull request #42 from contentstack/test/eco-2767
added test cases
2 parents 787409e + 1dc768d commit b91535d

File tree

9 files changed

+638
-0
lines changed

9 files changed

+638
-0
lines changed

playwright.config.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import type { PlaywrightTestConfig } from "@playwright/test";
2+
import { devices } from "@playwright/test";
3+
/**
4+
* Read environment variables from file.
5+
*/
6+
require("dotenv").config();
7+
8+
/**
9+
* See https://playwright.dev/docs/test-configuration.
10+
*/
11+
const config: PlaywrightTestConfig = {
12+
testMatch: /.*.ts/,
13+
/**
14+
* globalSetup & teardown of test data
15+
* globalTeardown: require.resolve("./tests/global-teardown"),
16+
*/
17+
globalSetup: require.resolve("./tests/global-setup"),
18+
19+
20+
testDir: "./tests/e2e",
21+
/* Maximum time one test can run for. */
22+
timeout: 10 * 10000,
23+
expect: {
24+
/**
25+
* Maximum time expect() should wait for the condition to be met.
26+
* For example in `await expect(locator).toHaveText();`
27+
*/
28+
timeout: 10 * 10000,
29+
},
30+
/* Fail the build on CI if you accidentally left test.only in the source code. */
31+
forbidOnly: !!process.env.CI,
32+
/* Retry on CI only */
33+
retries: 2,
34+
/* Opt out of parallel tests on CI. */
35+
workers: process.env.CI ? 1 : undefined,
36+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
37+
reporter: [["html", { open: "never" }]],
38+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
39+
use: {
40+
storageState: "storageState.json",
41+
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
42+
actionTimeout: 0,
43+
screenshot: "off",
44+
video: "off",
45+
viewport: { width: 1200, height: 720 },
46+
trace: "on-first-retry",
47+
baseURL: process.env.APP_HOST_URL,
48+
launchOptions: {
49+
logger: {
50+
isEnabled: () => {
51+
return false;
52+
},
53+
log: (name, severity, message, args) => console.log(`${name}: ${message}`),
54+
},
55+
},
56+
},
57+
/* Configure projects for major browsers */
58+
projects: [
59+
{
60+
name: "Chromium",
61+
use: {
62+
browserName: "chromium",
63+
},
64+
},
65+
{
66+
name: 'safari',
67+
use: { ...devices['Desktop Safari'] },
68+
},
69+
{
70+
name: "firefox",
71+
use: {
72+
browserName: "firefox",
73+
},
74+
},
75+
],
76+
};
77+
78+
export default config;

tests/e2e/json-editor.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { test, expect } from '@playwright/test';
2+
import { jsonEditorApp } from '../pages/jsoneditorpage';
3+
4+
test.describe(" Json editor App at entry", () => {
5+
let jsoneditorapp;
6+
7+
test('Should Apply all the Json app tests', async ({page}) => {
8+
jsoneditorapp = new jsonEditorApp(page);
9+
await jsoneditorapp.openJsonApp()
10+
await jsoneditorapp.fillJsonApp()
11+
await jsoneditorapp.formatJson()
12+
await jsoneditorapp.compactJson()
13+
await jsoneditorapp.transformJson()
14+
await jsoneditorapp.repairJson()
15+
await jsoneditorapp.undoAction()
16+
await jsoneditorapp.redoAction()
17+
});
18+
19+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const elements = {
2+
FrameLocator: '[data-testid="app-extension-frame"]',
3+
formatJsonName: 'Format JSON data, with proper indentation and line feeds (Ctrl+I)',
4+
compactJsonName: 'Compact JSON data, remove all whitespaces (Ctrl+Shift+I)',
5+
transformJsonName: 'Filter, sort, or transform contents' ,
6+
repairJsonName: 'Repair JSON: fix quotes and escape characters, remove comments and JSONP notation, turn JavaScript objects into JSON.',
7+
undoJsonAction:'Undo last action (Ctrl+Z)',
8+
redoJsonAction:'Redo (Ctrl+Shift+Z)'
9+
}

tests/global-setup.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// global-setup.ts
2+
import { chromium, FullConfig } from "@playwright/test";
3+
import { LoginPage } from "./login";
4+
import { getAuthToken } from "./utils/prehelpers";
5+
6+
async function globalSetup(config: FullConfig) {
7+
let loginPage: LoginPage;
8+
const browser = await chromium.launch();
9+
const page = await browser.newPage({
10+
httpCredentials: {
11+
username: process.env.BASIC_AUTH_USERNAME || "",
12+
password: process.env.BASIC_AUTH_PASSWORD || "",
13+
},
14+
});
15+
loginPage = new LoginPage(page);
16+
await loginPage.visitLoginPage();
17+
await loginPage.performLogin(process.env.EMAIL, process.env.PASSWORD);
18+
await getAuthToken();
19+
}
20+
21+
export default globalSetup;

tests/global-teardown.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { chromium, FullConfig } from "@playwright/test";
2+
const fs = require("fs");
3+
4+
async function globalTeardown(config: FullConfig) {
5+
await fs.unlink("data.json", (err) => {
6+
if (err) {
7+
throw err;
8+
}
9+
});
10+
}
11+
12+
export default globalTeardown;

tests/login.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { expect, Locator, Page } from "@playwright/test";
2+
3+
export class LoginPage {
4+
// Define selectors
5+
readonly page: Page;
6+
readonly emailInput: Locator;
7+
readonly passwordInput: Locator;
8+
readonly venusPasswordInput: Locator;
9+
readonly loginButton: Locator;
10+
11+
// Initialize selectors using constructor
12+
constructor(page: Page) {
13+
this.page = page;
14+
this.emailInput = page.locator("input[name='email']");
15+
this.passwordInput = page.locator("input[name='password']");
16+
this.venusPasswordInput = page.locator("input[name='password']");
17+
this.loginButton = page.locator('button:has-text("Log In"), button:has-text("LOGIN")');
18+
}
19+
20+
// Define methods
21+
async visitLoginPage() {
22+
if (process.env.ENV_URL) {
23+
await this.page.goto(`${process.env.ENV_URL}#!/login`);
24+
}
25+
}
26+
27+
// login handler
28+
async performLogin(email, password) {
29+
try {
30+
if ((await this.page.$(".user-session-page")) !== null) {
31+
// Contentstack classic UI login
32+
await this.emailInput.type(email);
33+
await this.passwordInput.type(password);
34+
await this.loginButton.click();
35+
await this.page.click(".user-name");
36+
await this.page.click("text=New Interface");
37+
await this.page.click(".OrgDropdown");
38+
await this.page.click(`#${process.env.ORG_ID}`);
39+
await this.page.waitForTimeout(2000);
40+
await this.page.context().storageState({ path: "storageState.json" });
41+
} else {
42+
await this.emailInput.type(email);
43+
await this.venusPasswordInput.type(password);
44+
const venusLoginButton = await this.page.waitForSelector('button:has-text("Log In")');
45+
await venusLoginButton.click();
46+
await this.page.waitForTimeout(2000);
47+
await this.page.context().storageState({ path: "storageState.json" });
48+
}
49+
} catch (e) {
50+
console.error(e);
51+
}
52+
}
53+
}

tests/pages/jsoneditorpage.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Locator, Page, Frame } from '@playwright/test';
2+
import { elements } from '../elements/jsoneditor.elements';
3+
4+
export class jsonEditorApp {
5+
readonly page: Page;
6+
constructor(page: Page) {
7+
this.page = page;
8+
}
9+
10+
async openJsonApp() {
11+
await this.page
12+
.goto(`${process.env.APP_BASE_URL}/#!/stack/${process.env.STACK_UID}/content-type/test_json_editor/en-us/entry/create`);
13+
14+
}
15+
16+
async fillJsonApp(){
17+
18+
await this.page.frameLocator('[data-testid="app-extension-frame"]').locator('div').filter({ hasText: /^\{\}$/ }).nth(1).click();
19+
20+
await this.page
21+
.frameLocator('[data-testid="app-extension-frame"]')
22+
.getByRole('textbox')
23+
.press('ArrowLeft');
24+
25+
await this.page
26+
.frameLocator(elements.FrameLocator)
27+
.getByRole('textbox')
28+
.fill('date : today');
29+
30+
await this.page
31+
.frameLocator(elements.FrameLocator)
32+
.locator('div')
33+
.filter({ hasText: /^\{date : today\}$/ })
34+
.nth(1)
35+
.click();
36+
}
37+
38+
async formatJson(){
39+
await this.page
40+
.frameLocator(elements.FrameLocator)
41+
.getByRole('button', { name: elements.formatJsonName })
42+
.click();
43+
}
44+
45+
async compactJson(){
46+
await this.page
47+
.frameLocator(elements.FrameLocator)
48+
.getByRole('button', { name: elements.compactJsonName })
49+
.click();
50+
}
51+
52+
async transformJson(){
53+
await this.page
54+
.frameLocator(elements.FrameLocator)
55+
.getByRole('button', { name: elements.transformJsonName})
56+
.click();
57+
}
58+
59+
async repairJson(){
60+
await this.page
61+
.frameLocator(elements.FrameLocator)
62+
.getByRole('button', { name: elements.repairJsonName })
63+
.click();
64+
}
65+
66+
async undoAction(){
67+
await this.page
68+
.frameLocator(elements.FrameLocator)
69+
.getByRole('button', { name: elements.undoJsonAction })
70+
.click();
71+
}
72+
73+
async redoAction(){
74+
await this.page
75+
.frameLocator(elements.FrameLocator)
76+
.getByRole('button', { name: elements.redoJsonAction })
77+
.click();
78+
}
79+
80+
};

tests/utils/posthelpers.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// module dependencies
2+
const axios = require("axios");
3+
4+
// deletes the created test app during tear down
5+
export const deleteApp = async (token, appId) => {
6+
let options = {
7+
url: `https://${process.env.DEVELOPER_HUB_API}/apps/${appId}`,
8+
method: "DELETE",
9+
headers: {
10+
"Content-Type": "application/json",
11+
organization_uid: process.env.ORG_ID,
12+
authtoken: token,
13+
},
14+
};
15+
try {
16+
let result = await axios(options);
17+
return result.data;
18+
} catch (error) {
19+
return error;
20+
}
21+
};
22+
// deletes the created content type during tear down
23+
export const deleteContentType = async (
24+
authToken: string | undefined,
25+
stackApiKey: string | undefined,
26+
mgmtToken: string | undefined,
27+
contentTypeID: string | undefined
28+
) => {
29+
let options = {
30+
url: `https://${process.env.BASE_API_URL}/v3/content_types/${contentTypeID}?`,
31+
method: "DELETE",
32+
headers: {
33+
api_key: stackApiKey,
34+
authtoken: authToken,
35+
authorization: mgmtToken,
36+
},
37+
};
38+
try {
39+
let result = await axios(options);
40+
return result.data;
41+
} catch (error) {
42+
return error;
43+
}
44+
};
45+
//deletes release
46+
export const deleteRelease = async (
47+
authToken: string | undefined,
48+
stackApiKey: string | undefined,
49+
mgmtToken: string | undefined,
50+
releaseID: string | undefined
51+
) => {
52+
let options = {
53+
url: `https://${process.env.BASE_API_URL}/v3/releases/${releaseID}`,
54+
method: "DELETE",
55+
headers: {
56+
api_key: stackApiKey,
57+
authtoken: authToken,
58+
authorization: mgmtToken,
59+
"Content-type": "application/json",
60+
},
61+
};
62+
try {
63+
let result = await axios(options);
64+
return result.data;
65+
} catch (error) {
66+
return error;
67+
}
68+
};

0 commit comments

Comments
 (0)