Browse Source

init

master
James Stout 6 years ago
commit
c2251d1a99
  1. 37
      .gitignore
  2. 25
      .travis.yml
  3. 36
      .vscode/launch.json
  4. 14
      .vscode/settings.json
  5. 20
      .vscode/tasks.json
  6. 13
      .vscodeignore
  7. 0
      CHANGELOG.md
  8. 7
      ISSUE_TEMPLATE.md
  9. 44
      LICENSE
  10. 22
      README.md
  11. BIN
      images/demo.gif
  12. BIN
      images/howto.png
  13. BIN
      images/icon.png
  14. 4207
      package-lock.json
  15. 95
      package.json
  16. 188
      src/extension/api/controller.ts
  17. 43
      src/extension/api/item.ts
  18. 55
      src/extension/commands.ts
  19. 19
      src/extension/index.ts
  20. 285
      test/commands/new-jekyll-file.test.ts
  21. 1
      test/fixtures/file-1.rb
  22. 1
      test/fixtures/file-2.rb
  23. 23
      test/index.ts
  24. 16
      tsconfig.json
  25. 18
      tslint.json

37
.gitignore vendored

@ -0,0 +1,37 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
node_modules
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
out
.vscode-test

25
.travis.yml

@ -0,0 +1,25 @@
sudo: false
language: node_js
node_js:
- "6"
os:
- osx
- linux
before_install:
- if [ $TRAVIS_OS_NAME == "linux" ]; then
export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0;
sudo apt-get --assume-yes install libsecret-1-0;
sh -e /etc/init.d/xvfb start;
sleep 3;
fi
install:
- npm install
- npm run vscode:prepublish
script:
- npm test --silent

36
.vscode/launch.json vendored

@ -0,0 +1,36 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/src/**/*.js"
],
"preLaunchTask": "npm: watch"
},
{
"name": "Launch Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test"
],
"outFiles": [
"${workspaceFolder}/out/test/**/*.js"
],
"preLaunchTask": "npm: watch"
}
]
}

14
.vscode/settings.json vendored

@ -0,0 +1,14 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
"spellright.language": "en",
"spellright.documentTypes": [
"latex",
"plaintext"
]
}

20
.vscode/tasks.json vendored

@ -0,0 +1,20 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

13
.vscodeignore

@ -0,0 +1,13 @@
.vscode/**
.vscode-test/**
typings/**
out/test/**
test/**
src/**
**/*.map
.gitignore
tsconfig.json
vsc-extension-quickstart.md
tsd.json
*.vsix
releases/**

0
CHANGELOG.md

7
ISSUE_TEMPLATE.md

@ -0,0 +1,7 @@
- VSCode Version:
- OS Version:
Steps to Reproduce:
1.
2.

44
LICENSE

@ -0,0 +1,44 @@
The MIT License (MIT)
Copyright (c) 2016 Steffen Leistner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The MIT License (MIT)
Copyright (c) 2018 James Stout
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

22
README.md

@ -0,0 +1,22 @@
# Jekyll Utils - Visual Studio Code Extension
A convenient way of creating new Jekyll post files.
> Inspired by [Sidebar Enhancements](https://github.com/titoBouzout/SideBarEnhancements) for Sublime.
> and [vscode-fileutils](https://github.com/sleistner/vscode-fileutils)
## Commands
```json
[
{
"command": "fileutils.newFileAtRoot",
"category": "File",
"title": "New Jekyll Post File Relative to Current View"
}
]
```
# License
MIT

BIN
images/demo.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 KiB

BIN
images/howto.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
images/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

4207
package-lock.json generated

File diff suppressed because it is too large Load Diff

95
package.json

@ -0,0 +1,95 @@
{
"name": "jekyll-post-file-generator",
"displayName": "Jekyll Utils",
"description": "A convenient way of creating new Jekyll post files",
"version": "0.0.1",
"license": "MIT",
"publisher": "stoutyhk",
"engines": {
"vscode": "^1.20.0"
},
"categories": [
"Other"
],
"icon": "images/icon.png",
"galleryBanner": {
"color": "#5c2d91",
"theme": "dark"
},
"bugs": {
"url": "https://bitbucket.org/stouty/vscode-jekyll-post-file-generator/admin/issues"
},
"repository": {
"type": "git",
"url": "https://stouty@bitbucket.org/stouty/vscode-jekyll-post-file-generator.git"
},
"homepage": "https://bitbucket.org/stouty/vscode-jekyll-post-file-generator/src",
"activationEvents": [
"onCommand:fileutils.newJekyllFile"
],
"main": "./out/src/extension",
"contributes": {
"commands": [
{
"command": "fileutils.newJekyllFile",
"category": "File",
"title": "New Jekyll Post File Relative to Current View"
}
],
"configuration": {
"type": "object",
"title": "Fileutils configuration",
"properties": {
"fileutils.delete.useTrash": {
"type": "boolean",
"default": false,
"description": "Move file to the recycle bin instead of deleting it permanently."
},
"fileutils.delete.confirm": {
"type": "boolean",
"default": true,
"description": "Controls if it should ask for confirmation when deleting a file."
}
}
}
},
"scripts": {
"vscode:prepublish": "./node_modules/.bin/tsc -p ./",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "npm run compile && node ./node_modules/vscode/bin/test",
"lint": "tslint -e './node_modules/**/*.ts' -e './typings/**/*.ts' './**/*.ts'",
"validate": "nsp check"
},
"devDependencies": {
"@types/bluebird-retry": "0.11.0",
"@types/chai": "^4.1.2",
"@types/mocha": "^2.2.48",
"@types/node": "^9.4.6",
"@types/sinon": "^4.1.3",
"@types/sinon-chai": "^2.7.29",
"bluebird-retry": "^0.11.0",
"chai": "^4.1.2",
"hoek": "^5.0.3",
"nsp": "^3.2.1",
"precommit-hook-eslint": "^3.0.0",
"sinon": "^4.4.2",
"sinon-chai": "^2.14.0",
"tslint": "^5.9.1",
"typescript": "^2.7.2",
"vscode": "^1.1.10"
},
"dependencies": {
"bluebird": "^3.5.1",
"fs-extra": "^5.0.0",
"trash": "^4.2.1",
"@types/fs-extra": "^5.0.0",
"@types/bluebird": "^3.5.20"
},
"pre-commit": [
"lint",
"validate",
"test"
]
}

188
src/extension/api/controller.ts

@ -0,0 +1,188 @@
import * as fs from 'fs';
import * as path from 'path';
import {
commands,
Position,
TextDocument,
TextEditor,
Uri,
ViewColumn,
window,
workspace,
WorkspaceConfiguration
} from 'vscode';
import { FileItem } from './item';
export interface IMoveFileDialogOptions {
prompt: string;
showFullPath?: boolean;
uri?: Uri;
}
export interface INewFileDialogOptions {
prompt: string;
relativeToRoot?: boolean;
}
export interface INewJekyllDialogOptions {
value: string;
valueSelection: [number, number];
}
export interface ICreateOptions {
fileItem: FileItem;
isDir?: boolean;
}
export class FileController {
public showNewJekyllFileDialog(options: INewJekyllDialogOptions): Promise<FileItem> {
const { value, valueSelection } = options;
let sourcePath = workspace.rootPath;
if (this.sourcePath) {
sourcePath = path.dirname(this.sourcePath);
}
if (!sourcePath) {
return Promise.reject(null);
}
return Promise.resolve(window.showInputBox({ value, valueSelection }))
.then((targetPath) => {
if (targetPath) {
targetPath = path.resolve(sourcePath, targetPath);
return new FileItem(sourcePath, targetPath);
}
});
}
public create(options: ICreateOptions): Promise<FileItem> {
const { fileItem, isDir = false } = options;
return this.ensureWritableFile(fileItem)
.then(() => fileItem.create(isDir))
.catch(() => Promise.reject(`Error creating file '${fileItem.targetPath}'.`));
}
public openFileInEditor(fileItem: FileItem): Promise<TextEditor> {
const isDir = fs.statSync(fileItem.path).isDirectory();
if (isDir) {
return;
}
return Promise.resolve(workspace.openTextDocument(fileItem.path))
.then((textDocument) => {
return textDocument
? Promise.resolve(textDocument)
: Promise.reject(new Error('Could not open file!'));
})
.then((textDocument) => window.showTextDocument(textDocument, ViewColumn.Active))
.then((editor) => {
const date: Date = new Date();
let monthDay: string = date.getDate().toString(); // Get the day as a number (1-31)
let month: number = date.getMonth(); // Get the month as a number (0-11) 2018-05-05 12:29:01 +0800
let hour: string = date.getHours().toString(); // Get the hour (0-23)
let mins: string = date.getMinutes().toString(); // Get the minute (0-59)
let sec: string = date.getSeconds().toString(); // Get the second (0-59)
const tzOffset: number = date.getTimezoneOffset();
const delim: string = '-';
const delimTime: string = ':';
const delimSpace: string = ' ';
const tzOffsetStr: string = ((tzOffset < 0 ? '+' : '-')
+ this.pad(parseInt(Math.abs(tzOffset / 60).toString(), 10), 2)
+ this.pad(Math.abs(tzOffset % 60), 2));
// console.log(tzOffsetStr);
const fullYear: string = date.getFullYear().toString();
if (monthDay.length === 1) {
monthDay = '0'.concat(monthDay);
}
if (hour.length === 1) {
hour = '0'.concat(hour);
}
if (mins.length === 1) {
mins = '0'.concat(mins);
}
if (sec.length === 1) {
sec = '0'.concat(sec);
}
month++;
let monthStr: string = month.toString();
if (monthStr.length === 1) {
monthStr = '0'.concat(monthStr);
}
// 2018-05-05 12:29:01 +0800
const outputStr: string = fullYear.concat(delim, monthStr, delim, monthDay, delimSpace,
hour, delimTime, mins, delimTime, sec, delimSpace, tzOffsetStr);
const frontMatter: string = '---\nlayout: single\ntitle: \ndate: '.
concat(outputStr, '\ncategories: \ntags: \nexcerpt: \n---\n');
const p: Position = new Position(0, 0);
editor.edit((editBuilder) => {
editBuilder.insert(p, frontMatter);
});
return editor
? Promise.resolve(editor)
: Promise.reject(new Error('Could not show document!'));
});
}
public closeCurrentFileEditor(): Thenable<any> {
return commands.executeCommand('workbench.action.closeActiveEditor');
}
private get sourcePath(): string {
const activeEditor: TextEditor = window.activeTextEditor;
const document: TextDocument = activeEditor && activeEditor.document;
return document && document.fileName;
}
private ensureWritableFile(fileItem: FileItem): Promise<FileItem> {
if (!fileItem.exists) {
return Promise.resolve(fileItem);
}
const message = `File '${fileItem.targetPath}' already exists.`;
const action = 'Overwrite';
return Promise.resolve(window.showInformationMessage(message, { modal: true }, action))
.then((overwrite) => overwrite ? Promise.resolve(fileItem) : Promise.reject(null));
}
private pad(num: number, length: number): string {
let str = '' + num;
while (str.length < length) {
str = '0' + str;
}
return str;
}
private get configuration(): WorkspaceConfiguration {
return workspace.getConfiguration('fileutils');
}
private get useTrash(): boolean {
return this.configuration.get('delete.useTrash');
}
private get confirmDelete(): boolean {
return this.configuration.get('delete.confirm');
}
}

43
src/extension/api/item.ts

@ -0,0 +1,43 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import { workspace } from 'vscode';
// tslint:disable-next-line
const trash = require('trash');
export class FileItem {
private SourcePath: string;
private TargetPath: string;
constructor(sourcePath: string, targetPath?: string) {
this.SourcePath = sourcePath;
this.TargetPath = targetPath;
}
get path(): string {
return this.SourcePath;
}
get targetPath(): string {
return this.TargetPath;
}
get exists(): boolean {
return fs.existsSync(this.targetPath);
}
public create(isDir: boolean = false): Promise<FileItem> {
const fn = isDir ? fs.ensureDir : fs.createFile;
return Promise.resolve(fs.remove(this.targetPath))
.then(() => fn(this.targetPath))
.then(() => new FileItem(this.targetPath));
}
private ensureDir(): Promise<any> {
return Promise.resolve(fs.ensureDir(path.dirname(this.targetPath)));
}
}

55
src/extension/commands.ts

@ -0,0 +1,55 @@
import {
Uri,
window
} from 'vscode';
import { FileController } from './api/controller';
function handleError(err) {
if (err) {
window.showErrorMessage(err);
}
return err;
}
export interface INewFileOptions {
relativeToRoot?: boolean;
}
export interface INewFolderOptions {
relativeToRoot?: boolean;
}
export const controller = new FileController();
export function newJekyllFile(options?: INewFileOptions) {
const { relativeToRoot = false } = options || {};
const delim: string = '-';
const ending: string = 'xxxxx.md';
const date: Date = new Date();
let monthDay: string = date.getDate().toString(); // Get the day as a number (1-31)
let month: number = date.getMonth(); // Get the month as a number (0-11) 2018-05-05-xxxxx.md
const fullYear: string = date.getFullYear().toString();
if (monthDay.length === 1) {
monthDay = '0'.concat(monthDay);
}
month++;
let monthStr: string = month.toString();
if (monthStr.length === 1) {
monthStr = '0'.concat(monthStr);
}
const outputStr: string = fullYear.concat(delim, monthStr, delim, monthDay, delim, ending);
// console.log(outputStr);
// console.log(date);
return controller.showNewJekyllFileDialog({ value: outputStr, valueSelection: [11, 16] })
.then((fileItem) => controller.create({ fileItem }))
.then((fileItem) => controller.openFileInEditor(fileItem))
.catch(handleError);
}

19
src/extension/index.ts

@ -0,0 +1,19 @@
import {
commands,
ExtensionContext
} from 'vscode';
import * as api from './commands';
function register(context: ExtensionContext, handler: any) {
const command = `fileutils.${handler.name}`;
const disposable = commands.registerCommand(command, handler);
context.subscriptions.push(disposable);
}
export function activate(context: ExtensionContext) {
register(context, api.newJekyllFile);
}

285
test/commands/new-jekyll-file.test.ts

@ -0,0 +1,285 @@
import * as retry from 'bluebird-retry';
import {
expect,
use as chaiUse
} from 'chai';
import * as fs from 'fs-extra';
import * as os from 'os';
import * as path from 'path';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import {
commands,
TextEditor,
Uri,
window,
workspace
} from 'vscode';
import {
controller,
newJekyllFile
} from '../../src/extension/commands';
chaiUse(sinonChai);
const rootDir = path.resolve(__dirname, '..', '..', '..');
const tmpDir = path.resolve(os.tmpdir(), 'vscode-fileutils-test--new-file');
const fixtureFile1 = path.resolve(rootDir, 'test', 'fixtures', 'file-1.rb');
const fixtureFile2 = path.resolve(rootDir, 'test', 'fixtures', 'file-2.rb');
const editorFile1 = path.resolve(tmpDir, 'nested-dir-1', 'nested-dir-2', 'file-1.rb');
const editorFile2 = path.resolve(tmpDir, 'file-2.rb');
const targetFile = path.resolve(`${editorFile1}.tmp`);
describe('newJekyllFile', () => {
beforeEach(() => Promise.all([
fs.remove(tmpDir),
fs.copy(fixtureFile1, editorFile1),
fs.copy(fixtureFile2, editorFile2)
]));
afterEach(() => fs.remove(tmpDir));
describe('with open text document', () => {
beforeEach(() => {
const openDocument = () => {
const uri = Uri.file(editorFile1);
return workspace.openTextDocument(uri)
.then((textDocument) => window.showTextDocument(textDocument));
};
const stubShowInputBox = () => {
const fileName = path.basename(targetFile);
sinon.stub(window, 'showInputBox').returns(Promise.resolve(fileName));
return Promise.resolve();
};
return Promise.all([
retry(() => openDocument(), { max_tries: 4, interval: 500 }),
stubShowInputBox()
]);
});
afterEach(() => {
const closeAllEditors = () => {
return commands.executeCommand('workbench.action.closeAllEditors');
};
const restoreShowInputBox = () => {
const stub: any = window.showInputBox;
return Promise.resolve(stub.restore());
};
return Promise.all([
closeAllEditors(),
restoreShowInputBox()
]);
});
it('prompts for file destination', () => {
return newJekyllFile().then(() => {
const delim: string = '-';
const ending: string = 'xxxxx.md';
const date: Date = new Date();
let monthDay: string = date.getDate().toString(); // Get the day as a number (1-31)
let month: number = date.getMonth(); // Get the month as a number (0-11) 2018-05-05-xxxxx.md
const fullYear: string = date.getFullYear().toString();
if (monthDay.length === 1) {
monthDay = '0'.concat(monthDay);
}
month++;
let monthStr: string = month.toString();
if (monthStr.length === 1) {
monthStr = '0'.concat(monthStr);
}
const outputStr: string = fullYear.concat(delim, monthStr, delim, monthDay, delim, ending);
const prompt = { value: outputStr, valueSelection: [11, 16] };
expect(window.showInputBox).to.have.been.calledWithExactly(prompt);
});
});
it('create file at destination', () => {
return newJekyllFile().then(() => {
const message = `${targetFile} does not exist`;
// tslint:disable-next-line:no-unused-expression
expect(fs.existsSync(targetFile), message).to.be.true;
});
});
it('opens new file as active editor', () => {
return newJekyllFile().then(() => {
const activeEditor: TextEditor = window.activeTextEditor;
expect(activeEditor.document.fileName).to.equal(targetFile);
});
});
// FIXME: controller#showNewFileDialog workspace root is undefined for some reason
// describe('relative to project root', () => {
// it('create file at destination', () => {
// return newFile({ relativeToRoot: true }).then(() => {
// const message = `${targetFile} does not exist`;
// expect(fs.existsSync(targetFile), message).to.be.true;
// });
// });
// });
describe('when target destination exists', () => {
beforeEach(() => {
const createTargetFile = () => {
return fs.copy(editorFile2, targetFile);
};
const stubShowInformationMessage = () => {
sinon.stub(window, 'showInformationMessage').returns(Promise.resolve(true));
return Promise.resolve();
};
return Promise.all([
createTargetFile(),
stubShowInformationMessage()
]);
});
afterEach(() => {
const stub: any = window.showInformationMessage;
return Promise.resolve(stub.restore());
});
it('asks to overwrite destination file', () => {
const message = `File '${targetFile}' already exists.`;
const action = 'Overwrite';
const options = { modal: true };
return newJekyllFile().then(() => {
expect(window.showInformationMessage).to.have.been.calledWith(message, options, action);
});
});
describe('responding with yes', () => {
it('overwrites the existig file', () => {
return newJekyllFile().then(() => {
const fileContent = fs.readFileSync(targetFile).toString();
expect(fileContent).to.equal('');
});
});
});
describe('responding with no', () => {
beforeEach(() => {
const stub: any = window.showInformationMessage;
stub.returns(Promise.resolve(false));
return Promise.resolve();
});
it('leaves existing file untouched', () => {
return newJekyllFile().then(() => {
const fileContent = fs.readFileSync(targetFile).toString();
expect(fileContent).to.equal('class FileTwo; end');
});
});
});
});
});
describe('with no open text document', () => {
beforeEach(() => {
const closeAllEditors = () => {
return commands.executeCommand('workbench.action.closeAllEditors');
};
const stubShowInputBox = () => {
sinon.stub(window, 'showInputBox');
return Promise.resolve();
};
return Promise.all([
closeAllEditors(),
stubShowInputBox()
]);
});
afterEach(() => {
const stub: any = window.showInputBox;
return Promise.resolve(stub.restore());
});
it('ignores the command call', () => {
return newJekyllFile().catch(() => {
// tslint:disable-next-line:no-unused-expression
expect(window.showInputBox).to.have.not.been.called;
});
});
});
describe('error handling', () => {
beforeEach(() => {
sinon.stub(controller, 'showNewJekyllFileDialog').returns(Promise.reject('must fail'));
sinon.stub(window, 'showErrorMessage');
return Promise.resolve();
});
afterEach(() => {
const restoreShowNewFileDialog = () => {
const stub: any = controller.showNewJekyllFileDialog;
return Promise.resolve(stub.restore());
};
const restoreShowErrorMessage = () => {
const stub: any = window.showErrorMessage;
return Promise.resolve(stub.restore());
};
return Promise.all([
restoreShowNewFileDialog(),
restoreShowErrorMessage()
]);
});
it('shows an error message', () => {
return newJekyllFile().catch((err) => {
expect(window.showErrorMessage).to.have.been.calledWithExactly('must fail');
});
});
});
});

1
test/fixtures/file-1.rb vendored

@ -0,0 +1 @@
class FileOne; end

1
test/fixtures/file-2.rb vendored

@ -0,0 +1 @@
class FileTwo; end

23
test/index.ts

@ -0,0 +1,23 @@
//
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
//
// This file is providing the test runner to use when running extension tests.
// By default the test runner in use is Mocha based.
//
// You can provide your own test runner if you want to override it by exporting
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
// host can call to run the tests. The test runner is expected to use console.log
// to report the results back to the caller. When the tests are finished, return
// a possible error to the callback or null if none.
const testRunner = require('vscode/lib/testrunner'); // tslint:disable-line
// You can directly control Mocha options by uncommenting the following lines
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
testRunner.configure({
reporter: 'list',
ui: 'bdd',
useColors: true
});
module.exports = testRunner;

16
tsconfig.json

@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": "."
},
"exclude": [
"node_modules",
".vscode-test"
]
}

18
tslint.json

@ -0,0 +1,18 @@
{
"extends": "tslint:latest",
"rules": {
"quotemark": [
true,
"single"
],
"trailing-comma": [
true,
"never"
],
"no-implicit-dependencies": [
true,
"dev"
],
"no-console": true
}
}
Loading…
Cancel
Save