James Stout
6 years ago
commit
c2251d1a99
25 changed files with 5169 additions and 0 deletions
@ -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 |
@ -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 |
@ -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" |
||||
} |
||||
] |
||||
} |
@ -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" |
||||
] |
||||
} |
@ -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 |
||||
} |
||||
} |
||||
] |
||||
} |
@ -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,0 +1,7 @@
|
||||
- VSCode Version: |
||||
- OS Version: |
||||
|
||||
Steps to Reproduce: |
||||
|
||||
1. |
||||
2. |
@ -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. |
||||
|
@ -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 |
After Width: | Height: | Size: 811 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 3.8 KiB |
@ -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" |
||||
] |
||||
} |
@ -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'); |
||||
} |
||||
} |
@ -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))); |
||||
} |
||||
|
||||
} |
@ -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); |
||||
} |
@ -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); |
||||
|
||||
} |
@ -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'); |
||||
}); |
||||
}); |
||||
|
||||
}); |
||||
|
||||
}); |
@ -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; |
@ -0,0 +1,16 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"module": "commonjs", |
||||
"target": "es6", |
||||
"outDir": "out", |
||||
"lib": [ |
||||
"es6" |
||||
], |
||||
"sourceMap": true, |
||||
"rootDir": "." |
||||
}, |
||||
"exclude": [ |
||||
"node_modules", |
||||
".vscode-test" |
||||
] |
||||
} |
Loading…
Reference in new issue