Skip to content

Commit

Permalink
feat(task): added shell task support
Browse files Browse the repository at this point in the history
fix #5
  • Loading branch information
Murat committed Mar 23, 2024
1 parent 45470f5 commit 8e838b9
Show file tree
Hide file tree
Showing 10 changed files with 584 additions and 15 deletions.
199 changes: 199 additions & 0 deletions src/__tests__/unit/tasks/shellTask.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
require('../../mocks/mockAll');
const mockSpawn = jest.spyOn(require('child_process'), 'spawn');

import { shellTask } from '../../../tasks/shellTask';
import { ShellTaskType } from '../../../types/mod.types';
import { variables } from '../../../variables';
import { mockPrompter } from '../../mocks/mockPrompter';

describe('shellTask', () => {
it('should run command', async () => {
mockSpawn.mockImplementationOnce(() => ({
on: (_event: string, cb: CallableFunction) => {
cb(0);
},
stdout: {
on: (_event: string, cb: CallableFunction) => {
cb('stdout');
},
},
stderr: {
on: (_event: string, cb: CallableFunction) => {
cb('stderr');
},
},
}));

const task: ShellTaskType = {
type: 'shell',
actions: [
{
name: 'test',
command: 'test',
},
],
};

await shellTask({
configPath: 'path/to/config',
task: task,
packageName: 'test-package',
});

expect(mockSpawn).toHaveBeenCalled();
expect(variables.get('test.output')).toBe('stdoutstderr');

mockSpawn.mockReset();
});
it('should run command with args', async () => {
mockSpawn.mockImplementationOnce(() => ({
on: (_event: string, cb: CallableFunction) => {
cb(0);
},
stdout: {
on: (_event: string, cb: CallableFunction) => {
cb('stdout');
},
},
stderr: {
on: (_event: string, cb: CallableFunction) => {
cb('stderr');
},
},
}));

const task: ShellTaskType = {
type: 'shell',
actions: [
{
name: 'test',
command: 'test',
args: ['arg1', 'arg2 arg3'],
},
],
};

await shellTask({
configPath: 'path/to/config',
task: task,
packageName: 'test-package',
});

expect(mockSpawn).toHaveBeenCalledWith('test', ['arg1', 'arg2 arg3']);

mockSpawn.mockReset();
});
it('should handle unexpected error', async () => {
mockSpawn.mockImplementationOnce(() => {
throw new Error('unexpected error');
});

const task: ShellTaskType = {
type: 'shell',
actions: [
{
name: 'test',
command: 'test',
},
],
};

await expect(
shellTask({
configPath: 'path/to/config',
task: task,
packageName: 'test-package',
})
).rejects.toThrowError('unexpected error');

expect(variables.get('test')).toEqual('error');

mockSpawn.mockReset();
});
it('should handle non zero exit code', async () => {
mockSpawn.mockImplementationOnce(() => ({
on: (_event: string, cb: CallableFunction) => {
cb(1);
},
stdout: {
on: (_event: string, cb: CallableFunction) => {
cb('stdout');
},
},
stderr: {
on: (_event: string, cb: CallableFunction) => {
cb('stderr');
},
},
}));

const task: ShellTaskType = {
type: 'shell',
actions: [
{
name: 'test',
command: 'test',
},
],
};

await expect(
shellTask({
configPath: 'path/to/config',
task: task,
packageName: 'test-package',
})
).rejects.toThrowError('non zero exit code');

expect(variables.get('test')).toEqual('error');

mockSpawn.mockReset();
});
it('should skip when condition does not meet', async () => {
const task: ShellTaskType = {
type: 'shell',
actions: [
{
when: { random: false },
name: 'test',
command: 'test',
},
],
};

await shellTask({
configPath: 'path/to/config',
task: task,
packageName: 'test-package',
});

expect(mockSpawn).not.toHaveBeenCalled();

mockSpawn.mockReset();
});
it('should skip when execution not allowed', async () => {
mockPrompter.confirm.mockClear();
mockPrompter.confirm.mockReturnValueOnce(false);

const task: ShellTaskType = {
type: 'shell',
actions: [
{
name: 'test',
command: 'test',
},
],
};

await shellTask({
configPath: 'path/to/config',
task: task,
packageName: 'test-package',
});

expect(mockSpawn).not.toHaveBeenCalled();

mockSpawn.mockReset();
mockPrompter.confirm.mockReset();
});
});
16 changes: 16 additions & 0 deletions src/__tests__/unit/utils/parseArgs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */

require('../../mocks/mockAll');

import { parseArgs } from '../../../utils/parseArgs';

describe('parseArgs', () => {
it('should parse args correctly', () => {
const args = parseArgs('arg1 "arg2 arg3" arg4');
expect(args).toEqual(['arg1', 'arg2 arg3', 'arg4']);
});
it('should parse escaped args correctly', () => {
const args = parseArgs('arg1 "arg\\"2 a\\"rg3"');
expect(args).toEqual(['arg1', 'arg"2 a"rg3']);
});
});
62 changes: 62 additions & 0 deletions src/schema/integrate.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,9 @@
{
"$ref": "#/definitions/FsTaskType"
},
{
"$ref": "#/definitions/ShellTaskType"
},
{
"$ref": "#/definitions/PromptTaskType"
}
Expand Down Expand Up @@ -1074,6 +1077,65 @@
],
"type": "object"
},
"ShellActionType": {
"additionalProperties": false,
"properties": {
"args": {
"items": {
"type": "string"
},
"type": "array"
},
"command": {
"type": "string"
},
"name": {
"type": "string"
},
"when": {
"$ref": "#/definitions/AnyObject"
}
},
"required": [
"command"
],
"type": "object"
},
"ShellTaskType": {
"additionalProperties": false,
"properties": {
"actions": {
"items": {
"$ref": "#/definitions/ShellActionType"
},
"type": "array"
},
"label": {
"type": "string"
},
"name": {
"type": "string"
},
"postInfo": {
"$ref": "#/definitions/TextOrTitleMessage"
},
"preInfo": {
"$ref": "#/definitions/TextOrTitleMessage"
},
"type": {
"const": "shell",
"type": "string"
},
"when": {
"$ref": "#/definitions/AnyObject"
}
},
"required": [
"actions",
"type"
],
"type": "object"
},
"StringsXmlTaskType": {
"additionalProperties": false,
"properties": {
Expand Down
62 changes: 62 additions & 0 deletions src/schema/upgrade.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,9 @@
{
"$ref": "#/definitions/FsTaskType"
},
{
"$ref": "#/definitions/ShellTaskType"
},
{
"$ref": "#/definitions/PromptTaskType"
}
Expand Down Expand Up @@ -1074,6 +1077,65 @@
],
"type": "object"
},
"ShellActionType": {
"additionalProperties": false,
"properties": {
"args": {
"items": {
"type": "string"
},
"type": "array"
},
"command": {
"type": "string"
},
"name": {
"type": "string"
},
"when": {
"$ref": "#/definitions/AnyObject"
}
},
"required": [
"command"
],
"type": "object"
},
"ShellTaskType": {
"additionalProperties": false,
"properties": {
"actions": {
"items": {
"$ref": "#/definitions/ShellActionType"
},
"type": "array"
},
"label": {
"type": "string"
},
"name": {
"type": "string"
},
"postInfo": {
"$ref": "#/definitions/TextOrTitleMessage"
},
"preInfo": {
"$ref": "#/definitions/TextOrTitleMessage"
},
"type": {
"const": "shell",
"type": "string"
},
"when": {
"$ref": "#/definitions/AnyObject"
}
},
"required": [
"actions",
"type"
],
"type": "object"
},
"StringsXmlTaskType": {
"additionalProperties": false,
"properties": {
Expand Down
Loading

0 comments on commit 8e838b9

Please sign in to comment.