Skip to content

Develop #145

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

Merged
merged 7 commits into from
May 12, 2025
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
16 changes: 9 additions & 7 deletions __test__/organizationFullPage.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { IOrgFullPageLocationInitData, LocationType } from "../src/types";
import { IGlobalFullPageLocationInitData, LocationType } from "../src/types";
import { OrganizationDetails } from "../src/types/organization.types";

const mockData: IOrgFullPageLocationInitData = {
type: LocationType.ORGANIZATION_FULL_PAGE,
const mockData: IGlobalFullPageLocationInitData = {
type: LocationType.GLOBAL_FULL_PAGE_LOCATION,
app_id: "app_id",
installation_uid: "installation_uid",
extension_uid: "extension_uid",
region: "NA",
endpoints:{CMA:"",APP:"",DEVELOPER_HUB:""},
endpoints: { CMA: "", APP: "", DEVELOPER_HUB: "" },
stack: {} as any,
user: {} as any,
currentBranch: "currentBranch",
Expand All @@ -22,16 +22,18 @@ afterEach(() => {
});

test("should return organization details", () => {
expect(organizationFullPage.currentOrganization).toBe(mockData.organization);
expect(organizationFullPage.currentOrganization).toBe(
mockData.organization
);
});

test("should handle missing organization details", () => {
const invalidData: IOrgFullPageLocationInitData = {
const invalidData: IGlobalFullPageLocationInitData = {
...mockData,
organization: null as any, // check missing organization details
};
const invalidOrganizationFullPage = {
currentOrganization: invalidData.organization,
};
expect(invalidOrganizationFullPage.currentOrganization).toBeNull();
});
});
154 changes: 91 additions & 63 deletions __test__/uiLocation.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { AxiosRequestConfig, AxiosResponse } from "axios";

import postRobot from "post-robot";

import UiLocation from "../src/uiLocation";
Expand All @@ -12,9 +14,8 @@ import {
LocationType,
Region,
} from "../src/types";
import { RequestOption } from '../src/types/common.types';
import { RequestConfig } from '../src/types/api.type';
import { AxiosRequestConfig, AxiosResponse } from 'axios';
import { RequestOption } from "../src/types/common.types";
import { RequestConfig } from "../src/types/api.type";

jest.mock("post-robot");
jest.mock("wolfy87-eventemitter");
Expand Down Expand Up @@ -60,7 +61,11 @@ const initData: IAppConfigInitData = {
installation_uid: "installation_uid",
extension_uid: "extension_uid",
region: "NA",
endpoints: { CMA: "https://api.contentstack.io", APP: "https://app.contentstack.app",DEVELOPER_HUB:"" },
endpoints: {
CMA: "https://api.contentstack.io",
APP: "https://app.contentstack.app",
DEVELOPER_HUB: "",
},
stack: mockStackData,
user: {} as any,
currentBranch: "currentBranch",
Expand Down Expand Up @@ -135,43 +140,48 @@ describe("UI Location", () => {
});
});

describe('dispatchPostRobotRequest', () => {
describe("dispatchPostRobotRequest", () => {
let mockPostRobot: typeof postRobot;
let opts: RequestOption;
let uiLocationInstance: UiLocation;
let onError: jest.Mock;

beforeEach(() => {
mockPostRobot = postRobot;
opts = { method: 'GET' };
opts = { method: "GET" };
uiLocationInstance = new UiLocation(initData);
onError = jest.fn();
uiLocationInstance.api = jest.fn().mockResolvedValue({
method: 'GET',
url: "https://test.com/test?limit=10&skip=0"
method: "GET",
url: "https://test.com/test?limit=10&skip=0",
});
});

it('should call sendToParent with the correct arguments and resolve with data', async () => {
it("should call sendToParent with the correct arguments and resolve with data", async () => {
const mockData = { success: true };
// Call the method that uses uiLocationInstance.api
const result = await uiLocationInstance.api("https://test.com/test?limit=10&skip=0",{
method: 'GET'
});
const result = await uiLocationInstance.api(
"https://test.com/test?limit=10&skip=0",
{
method: "GET",
}
);

// Assertions
expect(uiLocationInstance.api).toHaveBeenCalledWith('https://test.com/test?limit=10&skip=0',{
method: 'GET'
});
expect(uiLocationInstance.api).toHaveBeenCalledWith(
"https://test.com/test?limit=10&skip=0",
{
method: "GET",
}
);
expect(result).toEqual({
method: 'GET',
url: 'https://test.com/test?limit=10&skip=0',
method: "GET",
url: "https://test.com/test?limit=10&skip=0",
});

});

it('should call onError if sendToParent rejects', async () => {
const mockError = new Error('Test error');
it("should call onError if sendToParent rejects", async () => {
const mockError = new Error("Test error");

// Mock the api method to reject with an error
uiLocationInstance.api = jest.fn().mockRejectedValue(mockError);
Expand All @@ -182,84 +192,102 @@ describe("UI Location", () => {
});

// Call the method that uses uiLocationInstance.api and expect it to throw an error
await expect(uiLocationInstance.api("https://test.com/test?limit=10&skip=0",{
method: 'GET'
})).rejects.toThrow('Test error');
await expect(
uiLocationInstance.api(
"https://test.com/test?limit=10&skip=0",
{
method: "GET",
}
)
).rejects.toThrow("Test error");
});
});


describe("createSDKAdapter", () => {
let mockPostRobot: typeof postRobot;
let opts: RequestConfig;
let uiLocationInstance: UiLocation;
let onError: jest.Mock;

beforeEach(() => {
mockPostRobot = postRobot;
opts = { method: 'GET', baseURL: "https://test.com", url: "/test?limit10&skip=0" };
opts = {
method: "GET",
baseURL: "https://test.com",
url: "/test?limit10&skip=0",
};
uiLocationInstance = new UiLocation(initData);
onError = jest.fn();
uiLocationInstance.createAdapter = jest.fn().mockImplementation(() => async (config: AxiosRequestConfig) => {
return {
method: 'GET',
url: '/test?limit=10&skip=0',
baseURL: 'https://test.com',
data: {}
} as unknown as AxiosResponse;
});
uiLocationInstance.createAdapter = jest
.fn()
.mockImplementation(
() => async (config: AxiosRequestConfig) => {
return {
method: "GET",
url: "/test?limit=10&skip=0",
baseURL: "https://test.com",
data: {},
} as unknown as AxiosResponse;
}
);
});

afterEach(() => {
postRobotOnMock.mockClear();
postRobotSendToParentMock.mockClear();

jest.clearAllMocks();
window["postRobot"] = undefined;
window["iframeRef"] = undefined;
});
it('should call createAdapter with the correct arguments and resolve with data', async () => {

it("should call createAdapter with the correct arguments and resolve with data", async () => {
const mockData = { success: true };
// Call the method that uses uiLocationInstance.createAdapter
const result = await uiLocationInstance.createAdapter()({
method: 'GET',
url: '/test?limit=10&skip=0',
baseURL: 'https://test.com',
data: {}
method: "GET",
url: "/test?limit=10&skip=0",
baseURL: "https://test.com",
data: {},
});

expect(result).toEqual({
method: 'GET',
url: '/test?limit=10&skip=0',
baseURL: 'https://test.com',
data: {}
method: "GET",
url: "/test?limit=10&skip=0",
baseURL: "https://test.com",
data: {},
});
});
it('should call onError if createAdapter rejects', async () => {
const mockError = new Error('Test error');

it("should call onError if createAdapter rejects", async () => {
const mockError = new Error("Test error");

// Mock the createAdapter method to reject with an error
uiLocationInstance.createAdapter = jest.fn().mockImplementation(() => async (config: AxiosRequestConfig) => {
throw mockError;
});

uiLocationInstance.createAdapter = jest
.fn()
.mockImplementation(
() => async (config: AxiosRequestConfig) => {
throw mockError;
}
);

// Mock the onError implementation
onError.mockImplementation((error) => {
throw error;
});

// Call the method that uses uiLocationInstance.createAdapter and expect it to throw an error
await expect(uiLocationInstance.createAdapter()({
method: 'GET',
url: '/test?limit=10&skip=0',
baseURL: 'https://test.com',
data: {}
})).rejects.toThrow('Test error');
await expect(
uiLocationInstance.createAdapter()({
method: "GET",
url: "/test?limit=10&skip=0",
baseURL: "https://test.com",
data: {},
})
).rejects.toThrow("Test error");
});
});

describe("getConfig", () => {
it("should return config if no installation uid present", async () => {
const uiLocation = new UiLocation(initDataJsonRte as any);
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/app-sdk",
"version": "2.3.0",
"version": "2.3.1",
"types": "dist/src/index.d.ts",
"description": "The Contentstack App SDK allows you to customize your Contentstack applications.",
"main": "dist/index.js",
Expand Down
22 changes: 15 additions & 7 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { GenericObjectType } from "./types/common.types";
import { Entry } from "./types/entry.types";
import { Asset, ContentType, Schema, StackDetail } from "./types/stack.types";
import { OrganizationDetails } from "./types/organization.types";
import { ContentstackEndpoints } from './types/api.type';
import { ContentstackEndpoints } from "./types/api.type";
import { User } from "./types/user.types";
import Window from "./window";

Expand Down Expand Up @@ -69,7 +69,7 @@ export declare interface IAppConfigWidget {
stack: Stack;
}

export declare interface IOrgFullPageLocation {
export declare interface IGlobalFullPageLocation {
currentOrganization: OrganizationDetails;
}

Expand All @@ -88,7 +88,7 @@ export enum LocationType {
RTE = "RTE",
WIDGET = "WIDGET",
CONTENT_TYPE_SIDEBAR_WIDGET = "CONTENT_TYPE_SIDEBAR_WIDGET",
ORGANIZATION_FULL_PAGE = "ORGANIZATION_FULL_PAGE",
GLOBAL_FULL_PAGE_LOCATION = "GLOBAL_FULL_PAGE_LOCATION",
}

// Init data
Expand All @@ -106,10 +106,11 @@ declare interface ICommonInitData {
endpoints: ContentstackEndpoints;
}

export declare interface IOrgFullPageLocationInitData extends ICommonInitData {
export declare interface IGlobalFullPageLocationInitData
extends ICommonInitData {
organization: OrganizationDetails;
config?: GenericObjectType;
type: LocationType.ORGANIZATION_FULL_PAGE;
type: LocationType.GLOBAL_FULL_PAGE_LOCATION;
}

export declare interface IDashboardInitData extends ICommonInitData {
Expand Down Expand Up @@ -217,7 +218,7 @@ export type InitializationData =
| IRTEInitData
| ISidebarInitData
| IContentTypeSidebarInitData
| IOrgFullPageLocationInitData;
| IGlobalFullPageLocationInitData;

/**
* installation details API response
Expand Down Expand Up @@ -259,4 +260,11 @@ export enum Region {
GCP_EU = "GCP_EU",
}

export type RegionType = "UNKNOWN" | "NA" | "EU" | "AZURE_NA" | "AZURE_EU" | "GCP_NA" | string;
export type RegionType =
| "UNKNOWN"
| "NA"
| "EU"
| "AZURE_NA"
| "AZURE_EU"
| "GCP_NA"
| string;
Loading
Loading