Skip to content

Commit 4b22265

Browse files
committed
バリデーション用の関数を追加 (#8)
1 parent cf48fd3 commit 4b22265

File tree

8 files changed

+123
-43
lines changed

8 files changed

+123
-43
lines changed

package.nls.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@
3434
"quickpicks.tagQuickPick.title": "投稿に登録するタグを入力してください",
3535
"quickpicks.tagQuickPick.placeholder": "例) Rails React Mastodon",
3636
"quickpicks.tagQuickPick.item.description": "{0}件の投稿",
37+
"quickpicks.tagQuickPick.failure.validationError": "タグは1つ以上5つ以内で選択してください。",
3738

3839
"quickpicks.titleInputBox.title": "投稿のタイトルを入力してください",
3940
"quickpicks.titleInputBox.placeholder": "例) サルでもわかる!Rails入門",
40-
"quickpicks.titleInputBox.validationMessage": "タイトルは255文字まで有効です",
41+
"quickpicks.titleInputBox.validationMessage": "タイトルは1文字から255文字まで有効です",
42+
"quickpicks.titleInputBox.failure.validationError": "タイトルは1文字以上255文字以内で入力してください。",
4143

4244
"quickpicks.visibilityQuickPick.title": "投稿の公開範囲を選択してください",
4345
"quickpicks.visibilityQuickPick.public.label": "公開",

src/commands/compose.ts

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@ import { commands, Uri, window, workspace } from 'vscode';
33
import * as nls from 'vscode-nls';
44
import { client } from '../client';
55
import { configuration } from '../configuration';
6-
import { tagQuickPickCreator } from '../quickpicks/tagQuickPickCreator';
7-
import { titleInputBoxCreator } from '../quickpicks/titleInputBoxCreator';
6+
import { tagQuickPickCreator, validateTagQuickPick } from '../quickpicks/tagQuickPickCreator';
7+
import { titleInputBoxCreator, validateTitleInputBox } from '../quickpicks/titleInputBoxCreator';
88
import { privateLabel, visibilityQuickPickCreator } from '../quickpicks/visibilityQuickPickCreator';
9-
import { createMultiStepInput } from '../utils/createMultiStepInput';
109
import { handleErrorMessage } from '../utils/errorHandler';
1110
import { getFilenameFromPath } from '../utils/getFilenameFromPath';
1211

1312
const localize = nls.loadMessageBundle();
1413

15-
16-
1714
/**
1815
* アクティブなテキストエディタから投稿を公開するコマンドパレット向け関数
1916
* @param resource コマンドがexplorerから発火した際に渡される引数
@@ -32,10 +29,9 @@ export async function compose (resource?: { path: string }) {
3229
if (resource && resource.path) {
3330
options.title = getFilenameFromPath(resource.path);
3431
options.body = await workspace.openTextDocument(resource.path).then((document) => document.getText());
35-
}
3632

3733
// テキストエディタから発火した場合
38-
if (window.activeTextEditor) {
34+
} else if (window.activeTextEditor) {
3935
options.title = getFilenameFromPath(window.activeTextEditor.document.fileName);
4036
options.body = window.activeTextEditor.document.getText();
4137
}
@@ -44,26 +40,49 @@ export async function compose (resource?: { path: string }) {
4440
const tagsQuickPick = tagQuickPickCreator();
4541
const visibilityQuickPick = visibilityQuickPickCreator();
4642

43+
// ステップ数を追加
44+
[titleInputBox, tagsQuickPick, visibilityQuickPick].forEach((step, i, steps) => {
45+
step.totalSteps = steps.length;
46+
step.step = i;
47+
});
48+
4749
// titleInputBoxからタイトルを代入
4850
titleInputBox.onDidAccept(() => {
49-
options.title = titleInputBox.value;
51+
if (!validateTitleInputBox(titleInputBox)) {
52+
window.showErrorMessage(localize(
53+
'quickpicks.titleInputBox.failure.validationError',
54+
'タイトルは1文字以上255文字以内で入力してください。',
55+
));
56+
} else {
57+
options.title = titleInputBox.value;
58+
titleInputBox.hide();
59+
tagsQuickPick.show();
60+
}
5061
});
5162

5263
// tagQuickPickからタグを代入
5364
tagsQuickPick.onDidAccept(() => {
54-
options.tags = tagsQuickPick.selectedItems.map((item) => ({
55-
name: item.label,
56-
versions: [],
57-
}));
65+
if (!validateTagQuickPick(tagsQuickPick)) {
66+
window.showErrorMessage(localize(
67+
'quickpicks.tagQuickPick.failure.validationError',
68+
'タグは1つ以上5つ以内で選択してください。',
69+
));
70+
} else {
71+
options.tags = tagsQuickPick.selectedItems.map((item) => ({
72+
name: item.label,
73+
versions: [],
74+
}));
75+
tagsQuickPick.hide();
76+
visibilityQuickPick.show();
77+
}
5878
});
5979

6080
// visibilityQuickPick終了時に公開状態を代入してQiita.createItemを呼び出し
6181
visibilityQuickPick.onDidAccept(async () => {
6282
options.private = visibilityQuickPick.selectedItems[0].label === privateLabel;
83+
visibilityQuickPick.hide();
6384

6485
try {
65-
visibilityQuickPick.hide();
66-
6786
const item = await client.createItem(options);
6887

6988
const openInBrowser = localize(
@@ -82,16 +101,11 @@ export async function compose (resource?: { path: string }) {
82101
if (result === openInBrowser) {
83102
commands.executeCommand('vscode.open', Uri.parse(item.url));
84103
}
85-
86-
return;
87104
} catch (error) {
88-
return handleErrorMessage(error);
105+
handleErrorMessage(error);
89106
}
90107
});
91108

92-
createMultiStepInput([
93-
titleInputBox,
94-
tagsQuickPick,
95-
visibilityQuickPick,
96-
])();
109+
// 1つ目のステップから帰納的に呼び出し
110+
titleInputBox.show();
97111
}

src/commands/editTags.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Item } from 'qiita-js-2';
22
import { QuickPickItem, window } from 'vscode';
33
import * as nls from 'vscode-nls';
44
import { client } from '../client';
5-
import { makeQuickPickItemFromTag, tagQuickPickCreator } from '../quickpicks/tagQuickPickCreator';
5+
import { makeQuickPickItemFromTag, tagQuickPickCreator, validateTagQuickPick } from '../quickpicks/tagQuickPickCreator';
66
import { handleErrorMessage } from '../utils/errorHandler';
77

88
const localize = nls.loadMessageBundle();
@@ -26,21 +26,30 @@ export const updater = async (item: Item, selectedItems: ReadonlyArray<QuickPick
2626
*/
2727
export async function editTags (arg: object & { item: Item }) {
2828
const { item } = arg;
29-
const { tags: taggings } = arg.item;
29+
const taggings = arg.item.tags;
3030

31+
// 既存のタグを取得してselectedItemsとして初期化
3132
const selectedItems = await Promise.all(taggings.map(async (tagging) => {
3233
const tag = await client.fetchTag(tagging.name);
3334
return makeQuickPickItemFromTag(tag.id, tag.followers_count);
3435
}));
3536

36-
const input = tagQuickPickCreator(selectedItems);
37+
const quickPick = tagQuickPickCreator(selectedItems);
3738

38-
input.show();
39+
quickPick.show();
40+
41+
quickPick.onDidAccept(async () => {
42+
if (!validateTagQuickPick(quickPick)) {
43+
return window.showErrorMessage(localize(
44+
'quickpicks.tagQuickPick.failure.validationError',
45+
'タグは1つ以上5つ以内で選択してください。',
46+
));
47+
}
3948

40-
input.onDidAccept(async () => {
4149
try {
42-
input.hide();
43-
await updater(item, input.selectedItems);
50+
quickPick.hide();
51+
52+
await updater(item, quickPick.selectedItems);
4453

4554
return window.showInformationMessage(localize(
4655
'commands.editTags.success',

src/quickpicks/tagQuickPickCreator.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { SearchTagResult } from 'qiita-js-2';
2-
import { QuickPickItem, window } from 'vscode';
2+
import { QuickPick, QuickPickItem, window } from 'vscode';
33
import * as nls from 'vscode-nls';
44
import { client } from '../client';
55

@@ -20,6 +20,16 @@ export const makeQuickPickItemFromTag = (id: string, followersCount: number) =>
2020
),
2121
});
2222

23+
/**
24+
* タグのバリデーション
25+
* タグは1件以上5件以内のみ可能
26+
* @param selectedItems 選択済みアイテム
27+
* @return 真理値の結果
28+
*/
29+
export const validateTagQuickPick = (quickPick: QuickPick<QuickPickItem>) => {
30+
return quickPick.selectedItems.length >= 1 && quickPick.selectedItems.length <= 5;
31+
};
32+
2333
/**
2434
* ユーザーからの入力が検索結果に無いときにその入力を結果の先頭に挿入
2535
* @param value ユーザーの入力
@@ -73,8 +83,7 @@ export function tagQuickPickCreator (selectedItems?: QuickPickItem[]) {
7383
quickPick.busy = true;
7484
const suggestedItems = await suggestTags(value);
7585
quickPick.busy = false;
76-
77-
quickPick.items = quickPick.selectedItems.concat(suggestedItems);
86+
quickPick.items = quickPick.selectedItems.concat(suggestedItems);
7887
quickPick.selectedItems = quickPick.selectedItems;
7988
});
8089

src/quickpicks/titleInputBoxCreator.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
import { window } from 'vscode';
1+
import { InputBox, window } from 'vscode';
22
import * as nls from 'vscode-nls';
33

44
const localize = nls.loadMessageBundle();
55

6+
/**
7+
* タイトルのバリデーション 1文字以上255文字以内
8+
* @param value バリデートするタイトル
9+
* @return 結果を示す真理値
10+
*/
11+
export const validateTitleInputBox = (inputBox: InputBox) => {
12+
return inputBox.value.length >= 1 && inputBox.value.length <= 255;
13+
};
14+
615
/**
716
* タイトルを指定させるInputBoxを作成
817
* @param defaultValue 初期のタイトル
@@ -24,11 +33,13 @@ export function titleInputBoxCreator (defaultValue?: string) {
2433
);
2534

2635
inputBox.onDidChangeValue(() => {
27-
if (inputBox.value.length > 255) {
36+
if (!validateTitleInputBox(inputBox)) {
2837
inputBox.validationMessage = localize(
2938
'quickpicks.titleInputBox.validationMessage',
3039
'タイトルは255文字まで有効です',
3140
);
41+
} else {
42+
inputBox.validationMessage = '';
3243
}
3344
});
3445

src/test/quickpicks/tagsQuickPickCreator.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { expect } from 'chai';
2-
import { insertInputRaw, makeQuickPickItemFromTag } from '../../quickpicks/tagQuickPickCreator';
2+
import {
3+
insertInputRaw,
4+
makeQuickPickItemFromTag,
5+
tagQuickPickCreator,
6+
validateTagQuickPick,
7+
} from '../../quickpicks/tagQuickPickCreator';
38

49
describe('tagQuickPickCreator', () => {
510
it('渡されたIDと文字列からQuickPickItemを返す', () => {
@@ -20,4 +25,25 @@ describe('tagQuickPickCreator', () => {
2025

2126
expect(result).to.deep.include({ name: 'apple', url_name: 'apple', follower_count: 0, item_count: 0});
2227
});
28+
29+
it('選択されたアイテムが0件のときにバリデーションエラー', () => {
30+
const quickPick = tagQuickPickCreator([]);
31+
const result = validateTagQuickPick(quickPick);
32+
33+
expect(result).to.be.false;
34+
});
35+
36+
it('選択されたアイテムが5件以上のときにバリデーションエラー', () => {
37+
const quickPick = tagQuickPickCreator([
38+
{ label: 'apple', description: '1件の投稿' },
39+
{ label: 'microsoft', description: '2件の投稿' },
40+
{ label: 'facebook', description: '3件の投稿' },
41+
{ label: 'google', description: '4件の投稿' },
42+
{ label: 'intel', description: '5件の投稿' },
43+
{ label: 'sony', description: '6件の投稿' },
44+
]);
45+
const result = validateTagQuickPick(quickPick);
46+
47+
expect(result).to.be.false;
48+
});
2349
});
Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
import { expect } from 'chai';
2-
import { titleInputBoxCreator } from '../../quickpicks/titleInputBoxCreator';
2+
import {
3+
titleInputBoxCreator,
4+
validateTitleInputBox,
5+
} from '../../quickpicks/titleInputBoxCreator';
36

47
describe('titleInputBoxCreator', () => {
58
it('デフォルトの値を指定してInputBoxを作成', () => {
69
const inputBox = titleInputBoxCreator('タイトルです');
710
expect(inputBox.value).to.be.equal('タイトルです');
811
});
912

10-
// 値を直接変更したらonDidChangeValueが呼ばれないみたいです >_<
11-
// it('255文字以上の際に警告を表示', () => {
12-
// const inputBox = titleInputBoxCreator('0123456789'.repeat(26));
13-
// expect(inputBox.validationMessage).to.be.a('string');
14-
// });
13+
it('0文字の際にバリデーションエラー', () => {
14+
const inputBox = titleInputBoxCreator('');
15+
const result = validateTitleInputBox(inputBox);
16+
expect(result).to.be.false;
17+
});
18+
19+
it('255文字以上の際にバリデーションエラー', () => {
20+
const inputBox = titleInputBoxCreator('0123456789'.repeat(26));
21+
const result = validateTitleInputBox(inputBox);
22+
expect(result).to.be.false;
23+
});
1524
});

src/test/utils/compose.test.ts renamed to src/test/utils/getFilenameFromPath.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import { getFilenameFromPath } from '../../utils/getFilenameFromPath';
33

4-
describe('compose', () => {
4+
describe('getFilenameFromPath', () => {
55
it('パスから拡張子を除いたファイル名を取得', () => {
66
const path = 'file:///test.md';
77
const result = getFilenameFromPath(path);

0 commit comments

Comments
 (0)