Skip to content

Commit

Permalink
Merge 9aa1b23 into 2b3ee30
Browse files Browse the repository at this point in the history
  • Loading branch information
wsmd committed May 4, 2019
2 parents 2b3ee30 + 9aa1b23 commit aa3696e
Show file tree
Hide file tree
Showing 9 changed files with 3,758 additions and 82 deletions.
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
sudo: false
language: node_js
node_js:
- node
cache:
yarn: true
directories:
- node_modules
script:
- yarn run test:all
after_success:
- yarn run coveralls
16 changes: 8 additions & 8 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ function buildString(strings: ReadonlyArray<string>, replacements: ReadonlyArray
return result.trim();
}

function runScript<T>(strings: TemplateStringsArray, replacements: any[], options: Options & JXAOptions): Promise<T> {
function runScript<T>(
strings: TemplateStringsArray,
replacements: any[],
options: Options & JXAOptions = {},
): Promise<T> {
if (process.platform !== 'darwin') {
return Promise.reject(new Error('osascript-tag requires MacOS'));
return Promise.reject(new Error('osascript-tag requires macOS'));
}
return new Promise((resolve, reject) => {
const argv: any[] = options.argv || [];
Expand All @@ -68,11 +72,7 @@ function runScript<T>(strings: TemplateStringsArray, replacements: any[], option

if (options.language === 'JavaScript') {
language = options.language;
script = `
(function(...argv){
${script}
})(${argv.map(value => JSON.stringify(value))})
`;
script = `(function(...argv){${script}})(${argv.map(value => JSON.stringify(value))})`;
}

if (options.parse) {
Expand Down Expand Up @@ -127,7 +127,7 @@ function osascript<T>(
return runScript<T>(scriptOrOptions, replacementsArray, {});
}
return (script: TemplateStringsArray, ...replacements: any[]) => {
return runScript<T>(script, replacements, scriptOrOptions || {});
return runScript<T>(script, replacements, scriptOrOptions);
};
}

Expand Down
27 changes: 24 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,45 @@
"osx"
],
"scripts": {
"test": "echo \"no test specified\"",
"test": "jest",
"test:all": "yarn typecheck && yarn lint && yarn test:coverage",
"test:coverage": "jest --coverage",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"lint": "tslint --project .",
"typecheck": "tsc --noEmit",
"test:all": "yarn typecheck && yarn lint && yarn test",
"build": "tsc",
"build": "tsc --build tsconfig.build.json",
"prebuild": "rm -rf dist",
"prepack": "yarn test:all && yarn build"
},
"files": [
"dist"
],
"jest": {
"watchPathIgnorePatterns": [
"dist"
],
"transform": {
"^.+\\.ts?$": "ts-jest"
},
"collectCoverageFrom": [
"lib/**/*.ts"
],
"roots": [
"<rootDir>/lib/",
"<rootDir>/tests/"
]
},
"prettier": {
"singleQuote": true,
"trailingComma": "all",
"printWidth": 120
},
"devDependencies": {
"@types/jest": "^24.0.12",
"@types/node": "^11.13.8",
"coveralls": "^3.0.3",
"jest": "^24.7.1",
"ts-jest": "^24.0.2",
"tslint": "^5.16.0",
"typescript": "^3.4.5"
}
Expand Down
53 changes: 53 additions & 0 deletions tests/__mocks__/child_process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* tslint:disable variable-name */

import { ChildProcess } from 'child_process';
import { EventEmitter } from 'events';
import { Readable } from 'stream';

const spawn: jest.Mock<ChildProcess> & typeof mockMethods = jest.fn() as any;

beforeEach(() => spawn.__setOutput('default_stdout'));
afterEach(() => spawn.mockReset());

function createStream(value?: string | null) {
return new Readable({
read() {
if (value != null) {
this.push(Buffer.from(value));
this.destroy();
}
},
});
}

const mockMethods = {
__emitError(error?: any) {
const child = new EventEmitter() as ChildProcess;
spawn.mockImplementation(() => {
child.stdout = createStream();
child.stderr = createStream();
process.nextTick(() => child.emit('error', error));
return child;
});
},
__setOutput(stdoutData: string | null, stderrData?: string) {
const child = new EventEmitter() as ChildProcess;
const close = () => child.emit('close');
spawn.mockImplementation(() => {
child.stdout = createStream(stdoutData).on('close', close);
child.stderr = createStream(stderrData).on('close', close);
return child;
});
},
};

declare module 'child_process' {
namespace spawn {
export const __setOutput: typeof mockMethods['__setOutput'];
export const __emitError: typeof mockMethods['__emitError'];
}
}

Object.assign(spawn, mockMethods);

export { spawn };
39 changes: 39 additions & 0 deletions tests/osascript-jxa.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { spawn } from 'child_process';
import osascript from '../lib';

jest.mock('child_process');

beforeEach(() => {
Object.defineProperty(process, 'platform', { value: 'darwin' });
});

describe('osascript.jxa', () => {
it('calls osascript with correct arguments', async () => {
await osascript.jxa`test`;
const expectedArgs = ['-l', 'JavaScript', '-e', '(function(...argv){test})()'];
expect(spawn).toHaveBeenCalledWith('osascript', expectedArgs);
});

it('calls osascript with custom options', async () => {
await osascript.jxa({ flags: 'oe', argv: ['foo', 1] })`test`;
const expectedArgs = ['-l', 'JavaScript', '-s', 'oe', '-e', '(function(...argv){test})("foo",1)'];
expect(spawn).toHaveBeenCalledWith('osascript', expectedArgs);
});

test('returns parsed result', async () => {
spawn.__setOutput('{ "foo": 1, "bar": true, "baz": "baz"}');
const result = await osascript.jxa({ parse: true })`test`;
const expectedArgs = ['-l', 'JavaScript', '-s', 's', '-e', '(function(...argv){test})()'];
expect(spawn).toHaveBeenCalledWith('osascript', expectedArgs);
expect(result).toEqual({ foo: 1, bar: true, baz: 'baz' });
});

test('rejects when parsing fails', async () => {
spawn.__setOutput('not_valid_json');
try {
await osascript.jxa({ parse: true })``;
} catch (error) {
expect(error.message).toBe('Unexpected token o in JSON at position 1');
}
});
});
59 changes: 59 additions & 0 deletions tests/osascript.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { spawn } from 'child_process';
import osascript from '../lib';

jest.mock('child_process');

beforeEach(() => {
Object.defineProperty(process, 'platform', { value: 'darwin' });
});

describe('osascript', () => {
it('throws when not using macOS', () => {
const original = process.platform;
Object.defineProperty(process, 'platform', { value: 'lcars' });
expect(osascript``).rejects.toThrowError('osascript-tag requires macOS');
});

it('calls osascript', async () => {
try {
await osascript`test`;
} finally {
expect(spawn).toHaveBeenCalledTimes(1);
}
});

it('calls osascript with the default argument', async () => {
await osascript`test`;
// @ts-ignore
await osascript()`test`;
const expectedArgs = ['-l', 'AppleScript', '-e', 'test'];
expect(spawn).toHaveBeenNthCalledWith(1, 'osascript', expectedArgs);
expect(spawn).toHaveBeenNthCalledWith(2, 'osascript', expectedArgs);
});

it('calls osascript with custom argument', async () => {
await osascript({ flags: 'eh', language: 'JavaScript' })`test`;
const expectedArgs = ['-l', 'JavaScript', '-s', 'eh', '-e', expect.stringContaining('test')];
expect(spawn).toHaveBeenCalledWith('osascript', expectedArgs);
});

it('resolves with the script stdout', () => {
spawn.__setOutput('result');
expect(osascript`test`).resolves.toBe('result');
});

it('rejects with the script stderr', () => {
spawn.__setOutput(null, 'error');
expect(osascript`test`).rejects.toBe('error');
});

it('rejects with on child process error', () => {
spawn.__emitError('error');
expect(osascript`test`).rejects.toBe('error');
});

it('builds template strings', async () => {
await osascript`foo ${'bar'}`;
expect(spawn).toHaveBeenCalledWith('osascript', expect.arrayContaining(['foo bar']));
});
});
8 changes: 8 additions & 0 deletions tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"declaration": true
},
"include": ["lib"]
}
5 changes: 1 addition & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
"lib": ["es6"],
"strict": true,
"experimentalDecorators": true,
"esModuleInterop": true,
"declaration": true,
"outDir": "dist"
"esModuleInterop": true
},
"include": ["lib"],
"exclude": ["node_modules", "dist"]
}
Loading

0 comments on commit aa3696e

Please sign in to comment.