diff --git a/features/import.feature b/features/import.feature index f427290d28..3f1bbf3273 100644 --- a/features/import.feature +++ b/features/import.feature @@ -456,3 +456,42 @@ Feature: cli-kintone import command When I run the command with args "record import --base-url $$TEST_KINTONE_BASE_URL --app $APP_ID --api-token $API_TOKEN --guest-space-id 1 --file-path CliKintoneTest-61.csv" Then I should get the exit code is non-zero And The output error message should match with the pattern: "ERROR: \[403\] \[CB_NO02\] No privilege to proceed." + + Scenario: CliKintoneTest-62 Should import the records successfully with --encoding option is utf8 + Given The app "app_for_import" has no records + And The csv file "CliKintoneTest-62.csv" with "utf8" encoded content as below: + | Text | Number | + | レコード番号 | 10 | + And Load app ID of the app "app_for_import" as env var: "APP_ID" + And Load app token of the app "app_for_import" with exact permissions "add" as env var: "API_TOKEN" + When I run the command with args "record import --base-url $$TEST_KINTONE_BASE_URL --app $APP_ID --api-token $API_TOKEN --encoding utf8 --file-path CliKintoneTest-62.csv" + Then I should get the exit code is zero + And The app "app_for_import" should has records as below: + | Text | Number | + | レコード番号 | 10 | + + Scenario: CliKintoneTest-63 Should import the records successfully with --encoding option is sjis + Given The app "app_for_import" has no records + And The csv file "CliKintoneTest-63.csv" with "sjis" encoded content as below: + | Text | Number | + | 作成日時 | 10 | + And Load app ID of the app "app_for_import" as env var: "APP_ID" + And Load app token of the app "app_for_import" with exact permissions "add" as env var: "API_TOKEN" + When I run the command with args "record import --base-url $$TEST_KINTONE_BASE_URL --app $APP_ID --api-token $API_TOKEN --encoding sjis --file-path CliKintoneTest-63.csv" + Then I should get the exit code is zero + And The app "app_for_import" should has records as below: + | Text | Number | + | 作成日時 | 10 | + + Scenario: CliKintoneTest-64 Should return the error message when importing records with an unsupported character code. + Given The app "app_for_import" has no records + And The csv file "CliKintoneTest-64.csv" with content as below: + | Text | Number | + | Alice | 10 | + | Bob | 20 | + | Jenny | 30 | + And Load app ID of the app "app_for_import" as env var: "APP_ID" + And Load app token of the app "app_for_import" with exact permissions "add" as env var: "API_TOKEN" + When I run the command with args "record import --base-url $$TEST_KINTONE_BASE_URL --app $APP_ID --api-token $API_TOKEN --encoding unsupported_character_code --file-path CliKintoneTest-64.csv" + Then I should get the exit code is non-zero + And The output error message should match with the pattern: "Argument: encoding, Given: \"unsupported_character_code\", Choices: \"utf8\", \"sjis\"" diff --git a/features/step_definitions/import.ts b/features/step_definitions/import.ts index fdd44bc926..0f1b6780be 100644 --- a/features/step_definitions/import.ts +++ b/features/step_definitions/import.ts @@ -1,11 +1,24 @@ import * as assert from "assert"; import { Given, Then } from "../utils/world"; import fs from "fs"; +import type { SupportedEncoding } from "../utils/helper"; +import { SUPPORTED_ENCODING } from "../utils/helper"; Given( "The csv file {string} with content as below:", async function (filePath: string, table) { - await this.generateCsvFile(table.raw(), filePath); + await this.generateCsvFile(table.raw(), { filePath }); + }, +); + +Given( + "The csv file {string} with {string} encoded content as below:", + async function (filePath: string, encoding: SupportedEncoding, table) { + if (!SUPPORTED_ENCODING.includes(encoding)) { + throw new Error(`The encoding ${encoding} is not supported`); + } + + await this.generateCsvFile(table.raw(), { filePath, encoding }); }, ); diff --git a/features/utils/helper.ts b/features/utils/helper.ts index a70214cea5..db66145b81 100644 --- a/features/utils/helper.ts +++ b/features/utils/helper.ts @@ -1,6 +1,11 @@ import { spawnSync } from "child_process"; import path from "path"; import fs from "fs/promises"; +import iconv from "iconv-lite"; + +export const SUPPORTED_ENCODING = ["utf8", "sjis"]; + +export type SupportedEncoding = (typeof SUPPORTED_ENCODING)[number]; export const execCliKintoneSync = ( args: string, @@ -68,13 +73,13 @@ const inputEnvReplacer = (envVars: { [key: string]: string } | undefined) => { }; export const generateCsvFile = async ( - inputCsvObject: string[][], - options: { baseDir?: string; destFilePath?: string }, + csvContent: string, + options: { + baseDir?: string; + destFilePath?: string; + encoding?: SupportedEncoding; + }, ): Promise => { - const csvContent = inputCsvObject - .map((row) => row.map((field) => `"${field}"`).join(",")) - .join("\n"); - let filePath = options.destFilePath; if (filePath) { filePath = options.baseDir @@ -89,7 +94,7 @@ export const generateCsvFile = async ( filePath = path.join(tempDir, "records.csv"); } - await fs.writeFile(filePath, csvContent); + await _writeFile(csvContent, filePath, { encoding: options.encoding }); return filePath; }; @@ -97,17 +102,29 @@ export const generateCsvFile = async ( export const generateFile = async ( content: string, filePath: string, - options: { baseDir?: string }, + options: { baseDir?: string; encoding?: SupportedEncoding }, ): Promise => { const actualFilePath = options.baseDir ? path.join(options.baseDir, filePath) : filePath; await fs.mkdir(path.dirname(actualFilePath), { recursive: true }); - await fs.writeFile(actualFilePath, content); + await _writeFile(content, actualFilePath, { encoding: options.encoding }); return actualFilePath; }; +const _writeFile = async ( + content: string, + filePath: string, + options?: { encoding?: SupportedEncoding }, +): Promise => { + if (options && options.encoding) { + return fs.writeFile(filePath, iconv.encode(content, options.encoding)); + } + + return fs.writeFile(filePath, content); +}; + export const getRecordNumbers = (appId: string, apiToken: string): string[] => { const command = `record export --app ${appId} --base-url $$TEST_KINTONE_BASE_URL --api-token ${apiToken} --fields Record_number`; const response = execCliKintoneSync(command); diff --git a/features/utils/world.ts b/features/utils/world.ts index e88ca9f893..66322ae256 100644 --- a/features/utils/world.ts +++ b/features/utils/world.ts @@ -2,6 +2,7 @@ import type { SpawnSyncReturns } from "child_process"; import type { Credentials, AppCredential, Permission } from "./credentials"; import * as cucumber from "@cucumber/cucumber"; import { World } from "@cucumber/cucumber"; +import type { SupportedEncoding } from "./helper"; import { generateCsvFile, execCliKintoneSync, @@ -67,10 +68,18 @@ export class OurWorld extends World { }); } - public async generateCsvFile(inputCsvObject: string[][], filePath?: string) { - return generateCsvFile(inputCsvObject, { + public async generateCsvFile( + inputCsvObject: string[][], + options?: { filePath?: string; encoding?: SupportedEncoding }, + ) { + const csvContent = inputCsvObject + .map((row) => row.map((field) => `"${field}"`).join(",")) + .join("\n"); + + return generateCsvFile(csvContent, { baseDir: this.workingDir, - destFilePath: filePath, + destFilePath: options?.filePath, + encoding: options?.encoding, }); }