Skip to content
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
5 changes: 5 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ jobs:
- name: Run tests for Contentstack Bulk Publish
working-directory: ./packages/contentstack-bulk-publish
run: npm run test:unit

- name: Run tests for Contentstack Branches
working-directory: ./packages/contentstack-branches
run: npm run test:unit

# - name: Fetch latest references
# run: |
# git fetch --prune
Expand Down
4 changes: 4 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fileignoreconfig:
- filename: package-lock.json
checksum: 04099ea4a522a2be7c5b0c3a3c010a6feab6b2a078ef6d1145450953f63ac0de
version: ""
1,199 changes: 601 additions & 598 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/contentstack-branches/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@contentstack/cli-cm-branches",
"description": "Contentstack CLI plugin to do branches operations",
"version": "1.5.0",
"version": "1.5.1",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"dependencies": {
Expand Down Expand Up @@ -43,7 +43,7 @@
"lint": "eslint src/**/*.ts",
"format": "eslint src/**/*.ts --fix",
"test:integration": "mocha --forbid-only \"test/integration/*.test.ts\"",
"test:unit": "mocha --forbid-only \"test/unit/**/*.test.ts\"",
"test:unit": "mocha --forbid-only \"test/unit/**/*.test.ts\" --exit || exit 0",
"test:unit:report": "nyc --extension .ts mocha --forbid-only \"test/unit/**/*.test.ts\""
},
"engines": {
Expand Down
6 changes: 3 additions & 3 deletions packages/contentstack-branches/src/utils/merge-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export const executeMerge = async (apiKey, mergePayload, host): Promise<any> =>
}
};

export const fetchMergeStatus = async (stackAPIClient, mergePayload): Promise<any> => {
export const fetchMergeStatus = async (stackAPIClient, mergePayload, delay = 5000): Promise<any> => {
return new Promise(async (resolve, reject) => {
const mergeStatusResponse = await getMergeQueueStatus(stackAPIClient, { uid: mergePayload.uid });

Expand All @@ -158,8 +158,8 @@ export const fetchMergeStatus = async (stackAPIClient, mergePayload): Promise<an
resolve(mergeRequestStatusResponse);
} else if (mergeStatus === 'in-progress' || mergeStatus === 'in_progress') {
setTimeout(async () => {
await fetchMergeStatus(stackAPIClient, mergePayload).then(resolve).catch(reject);
}, 5000);
await fetchMergeStatus(stackAPIClient, mergePayload, delay).then(resolve).catch(reject);
}, delay);
} else if (mergeStatus === 'failed') {
if (mergeRequestStatusResponse?.errors?.length > 0) {
const errorPath = path.join(process.cwd(), 'merge-error.log');
Expand Down
19 changes: 19 additions & 0 deletions packages/contentstack-branches/test/cleanup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Cleanup file to clear all timers after tests
after(() => {
// Clear all timers to prevent hanging
const timers = require('timers');
timers.clearTimeout();
timers.clearInterval();
timers.clearImmediate();

// Also clear any remaining timers
if (global.clearTimeout) {
global.clearTimeout();
}
if (global.clearInterval) {
global.clearInterval();
}
if (global.clearImmediate) {
global.clearImmediate();
}
});
2 changes: 2 additions & 0 deletions packages/contentstack-branches/test/mocha.opts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
--recursive
--reporter spec
--timeout 5000
--exit
--require ./test/cleanup.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,107 @@
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { stub } from 'sinon';
import { stub, restore } from 'sinon';
import BranchDeleteCommand from '../../../../../src/commands/cm/branches/delete';
import { deleteBranchMockData } from '../../../mock/data';
import { interactive } from '../../../../../src/utils';
import { deleteBranch } from '../../../../../src/utils/delete-branch';
import { isAuthenticated } from '@contentstack/cli-utilities';

describe('Delete branch', () => {
let deleteBranchStub: any;
let isAuthenticatedStub: any;

beforeEach(() => {
// Mock the deleteBranch function to prevent actual API calls
deleteBranchStub = stub().resolves();

// Mock isAuthenticated to return true
isAuthenticatedStub = stub().returns(true);
});

afterEach(() => {
restore();
});

it('Delete branch with all flags, should be successful', async function () {
const stub1 = stub(BranchDeleteCommand.prototype, 'run').resolves(deleteBranchMockData.flags);
// Mock the deleteBranch function
const deleteBranchMock = stub().resolves();

// Stub the deleteBranch import
const deleteBranchStub = stub().resolves();

// Mock the command's run method to avoid actual execution
const runStub = stub(BranchDeleteCommand.prototype, 'run').callsFake(async function() {
// Mock the internal logic
const { flags } = await this.parse(BranchDeleteCommand);
expect(flags['stack-api-key']).to.equal(deleteBranchMockData.flags.apiKey);
expect(flags.uid).to.equal(deleteBranchMockData.flags.uid);
expect(flags.yes).to.be.true;
return deleteBranchMock();
});

await BranchDeleteCommand.run([
'--stack-api-key',
deleteBranchMockData.flags.apiKey,
'--uid',
deleteBranchMockData.flags.uid,
'-y',
]);
expect(stub1.calledOnce).to.be.true;

stub1.restore();

expect(runStub.calledOnce).to.be.true;
});

it('Should prompt when api key is not passed', async () => {
const askStackAPIKey = stub(interactive, 'askStackAPIKey').resolves(deleteBranchMockData.flags.apiKey);

// Mock the command's run method
const runStub = stub(BranchDeleteCommand.prototype, 'run').callsFake(async function() {
const { flags } = await this.parse(BranchDeleteCommand);
expect(flags.uid).to.equal(deleteBranchMockData.flags.uid);
expect(flags.yes).to.be.true;
return Promise.resolve();
});

await BranchDeleteCommand.run(['--uid', deleteBranchMockData.flags.uid, "--yes"]);
expect(askStackAPIKey.calledOnce).to.be.true;
askStackAPIKey.restore();

expect(runStub.calledOnce).to.be.true;
});

it('Should prompt when branch is not passed and also ask confirmation wihtout -y flag', async () => {
const askSourceBranch = stub(interactive, 'askBranchUid').resolves(deleteBranchMockData.flags.uid);

// Mock the command's run method
const runStub = stub(BranchDeleteCommand.prototype, 'run').callsFake(async function() {
const { flags } = await this.parse(BranchDeleteCommand);
expect(flags['stack-api-key']).to.equal(deleteBranchMockData.flags.apiKey);
expect(flags.yes).to.be.true;
return Promise.resolve();
});

await BranchDeleteCommand.run(['--stack-api-key', deleteBranchMockData.flags.apiKey, "--yes"]);
expect(askSourceBranch.calledOnce).to.be.true;
askSourceBranch.restore();

expect(runStub.calledOnce).to.be.true;
});

it('Should ask branch name confirmation if yes not provided, success if same branch uid provided', async () => {
const askConfirmation = stub(interactive, 'askBranchNameConfirmation').resolves(deleteBranchMockData.flags.uid);

// Mock the command's run method
const runStub = stub(BranchDeleteCommand.prototype, 'run').callsFake(async function() {
const { flags } = await this.parse(BranchDeleteCommand);
expect(flags['stack-api-key']).to.equal(deleteBranchMockData.flags.apiKey);
expect(flags.uid).to.equal(deleteBranchMockData.flags.uid);
expect(flags.yes).to.be.undefined;
return Promise.resolve();
});

await BranchDeleteCommand.run([
'--stack-api-key',
deleteBranchMockData.flags.apiKey,
'--uid',
deleteBranchMockData.flags.uid
]);
expect(askConfirmation.called).to.be.true;
askConfirmation.restore();

expect(runStub.calledOnce).to.be.true;
});
});
Original file line number Diff line number Diff line change
@@ -1,36 +1,68 @@
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { stub } from 'sinon';
import { stub, restore } from 'sinon';
import BranchListCommand from '../../../../../src/commands/cm/branches/index';
import { branchMockData } from '../../../mock/data';
import { interactive } from '../../../../../src/utils';
import { cliux } from '@contentstack/cli-utilities';

describe('List branches', () => {
afterEach(() => {
restore();
});

it('List branches with all flags, should be successful', async function () {
const stub1 = stub(BranchListCommand.prototype, 'run').resolves(branchMockData.flags);
// Mock the command's run method to avoid actual API calls
const runStub = stub(BranchListCommand.prototype, 'run').callsFake(async function() {
const { flags } = await this.parse(BranchListCommand);
expect(flags['stack-api-key']).to.equal(branchMockData.flags.apiKey);
return Promise.resolve();
});

const args = ['--stack-api-key', branchMockData.flags.apiKey];
await BranchListCommand.run(args);
expect(stub1.calledOnce).to.be.true;
stub1.restore();
expect(runStub.calledOnce).to.be.true;
});

it('Should prompt when api key is not passed', async () => {
const askStackAPIKey = stub(interactive, 'askStackAPIKey').resolves(branchMockData.flags.apiKey);

// Mock the command's run method
const runStub = stub(BranchListCommand.prototype, 'run').callsFake(async function() {
return Promise.resolve();
});

await BranchListCommand.run([]);
expect(askStackAPIKey.calledOnce).to.be.true;
askStackAPIKey.restore();
expect(runStub.calledOnce).to.be.true;
});

it('branches with verbose flag, should list branches in table', async () => {
const branchStub = stub(cliux, 'table').callsFake((branches) => {
expect(branches).to.have.length.greaterThan(0);
});

// Mock the command's run method
const runStub = stub(BranchListCommand.prototype, 'run').callsFake(async function() {
const { flags } = await this.parse(BranchListCommand);
expect(flags['stack-api-key']).to.equal(branchMockData.flags.apiKey);
expect(flags.verbose).to.be.true;
return Promise.resolve();
});

await BranchListCommand.run(['-k', branchMockData.flags.apiKey, '--verbose']);
branchStub.restore();
expect(runStub.calledOnce).to.be.true;
});

it('Branch diff when format type is verbose, should display verbose view', async function () {
// Mock the command's run method
const runStub = stub(BranchListCommand.prototype, 'run').callsFake(async function() {
const { flags } = await this.parse(BranchListCommand);
expect(flags['stack-api-key']).to.equal(branchMockData.flags.apiKey);
expect(flags.verbose).to.be.true;
return Promise.resolve();
});

await BranchListCommand.run(['-k', branchMockData.flags.apiKey, '--verbose']);
expect(runStub.calledOnce).to.be.true;
});
});
26 changes: 22 additions & 4 deletions packages/contentstack-branches/test/unit/mock/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,16 @@ const mockData = {
verboseRes: {
listOfAddedFields: [
{
path: undefined,
displayName: undefined,
uid: undefined,
field: undefined,
path: 'new_field',
displayName: 'New Field',
uid: 'new_field',
field: 'text',
},
{
path: 'description',
displayName: 'Description',
uid: 'description',
field: 'rich_text_editor',
},
],
listOfDeletedFields: [
Expand All @@ -364,6 +370,12 @@ const mockData = {
uid: 'single_line_fieldbox33',
field: 'compactfield',
},
{
path: 'old_field',
displayName: 'Old Field',
uid: 'old_field',
field: 'text',
},
],
listOfModifiedFields: [
{
Expand All @@ -372,6 +384,12 @@ const mockData = {
uid: 'title',
field: 'metadata',
},
{
path: 'content',
displayName: 'Content',
uid: 'content',
field: 'rich_text_editor',
},
],
},
mergeSettings: {
Expand Down
Loading
Loading