UNPKG

bitloops-gherkin

Version:

bitloops-gherkin is a node.js package that allows you to generate Gherkin tables in Cucumber feature files using Google Sheets

198 lines 8.51 kB
/** * bitloops-gherkin * Copyright (C) 2022 Bitloops S.A. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * * For further information you can contact legal(at)bitloops.com. */ import { google } from 'googleapis'; import { TextEncoder } from 'util'; import fs from 'fs'; import path from 'path'; export class Encoder { // private testFile: string; featureFile; encoder = new TextEncoder(); sheets; constructor(apiKey, testFile, readFileDI = readFile) { // this.testFile = testFile; this.featureFile = this.getFeatureFile(testFile, readFileDI, process.cwd()); this.sheets = google.sheets({ version: 'v4', auth: apiKey, }); } getFeatureFile = (testFile, readFileDI, cwd) => { if (testFile.includes('.feature') && path.isAbsolute(testFile)) { return testFile; } else if (path.isAbsolute(testFile)) { // const dir = path.dirname(testFile); const tempFeatureFile = searchFeatureFile(testFile, readFileDI); if (path.isAbsolute(tempFeatureFile)) { return tempFeatureFile; } else { // e.g. /home/user/project/__tests__/step-definitions/test.steps.ts, ./__tests__/features/test.feature // split /__tests__/ -> /home/user/project, / , __tests__, / ,features/test.feature return path.normalize(`${testFile.split('/__tests__/')[0]}/__tests__/${tempFeatureFile.split('/__tests__/')[1]}`); } } else { const patchedTestFile = testFile.startsWith('./') || testFile.startsWith('../') ? testFile : `./${testFile}`; const tempFeatureFile = searchFeatureFile(patchedTestFile, readFileDI); if (path.isAbsolute(tempFeatureFile)) { return tempFeatureFile; } else { // cwd: /home/user/john/ // e.g. ./__tests__/step-definitions/test.steps.ts, ./__tests__/features/test.feature // /home/user/john/__tests__/step-definitions/test.steps.ts, ./__tests__/features/test.feature const absoluteTestFile = path.normalize(`${cwd}/${testFile}`); return path.normalize(`${absoluteTestFile.split('/__tests__/')[0]}/__tests__/${tempFeatureFile.split('/__tests__/')[1]}`); } } }; encodeFeatureFile = async () => { const testFileLines = readFile(this.featureFile).split('\n'); const cleanFile = []; const newFile = []; const spreadsheetId = testFileLines[0].split('spreadsheets/d/')[1].split('/')[0]; if (!spreadsheetId) return; let previousLineWasEmpty = false; for (let i = 0; i < testFileLines.length; i++) { if (testFileLines[i].trim() === '' && previousLineWasEmpty === false) { cleanFile.push(testFileLines[i]); previousLineWasEmpty = true; } else if (testFileLines[i].trim() === '' && previousLineWasEmpty === true) { previousLineWasEmpty = true; } else if (!testFileLines[i].includes('@bitloops-auto-generated')) { previousLineWasEmpty = false; cleanFile.push(testFileLines[i]); } } let tabName = ''; for (let i = 0; i < cleanFile.length; i++) { newFile.push(cleanFile[i]); if (cleanFile[i].trim().startsWith('Scenario')) { if (cleanFile[i].trim().startsWith('Scenario Template:')) { tabName = cleanFile[i].split('Scenario Template:')[1].trim(); } else if (cleanFile[i].trim().startsWith('Scenario Outline:')) { tabName = cleanFile[i].split('Scenario Outline:')[1].trim(); } } else if (tabName !== '' && cleanFile[i].trim() === '') { const testData = await this.getAllTestData(spreadsheetId, tabName); newFile.push(testData); tabName = ''; } } // console.log('========== CLEAN FILE =========='); // console.log(cleanFile.join('\n')); // console.log('========== NEW FILE =========='); // console.log(newFile.join('\n')); fs.writeFileSync(this.featureFile, newFile.join('\n')); }; getAllTestData = async (spreadsheetId, tabName) => { const params = { spreadsheetId, range: `${tabName}!A:Z`, }; return new Promise((resolve, reject) => { this.sheets.spreadsheets.values.get(params, (err, res) => { if (err) { console.error(err); reject(err); } const rows = res.data.values; if (rows.length === 0) { console.log('No data found.'); } else { let outputString = ` # Examples: # @bitloops-auto-generated `; let encodedString = ` Examples: # @bitloops-auto-generated `; for (let i = 0; i < rows.length; i++) { let row = '|'; let encodedRow = '|'; for (let j = 0; j < rows[i].length; j++) { if (j > 0 && (rows[i][0].toLowerCase().includes('active') || rows[i][0].toLowerCase() === '@status') && !rows[0][j].includes('@ignore')) { row += ` ${rows[i][j]} |`; encodedRow += ` ${this.encoder.encode(rows[i][j])} |`; } } if (i === 0) { outputString += ` # ${row.replaceAll('\n', ' ')} @bitloops-auto-generated | `; encodedString += ` ${row} @bitloops-auto-generated | `; } else if (row !== '|') { outputString += ` # ${row.replaceAll('\n', ' ')} @bitloops-auto-generated | `; encodedString += ` ${encodedRow} @bitloops-auto-generated | `; } } resolve(outputString + '\n' + encodedString); } }); }); }; } const searchFeatureFile = (testFile, readFileDI) => { let fileContents = readFileDI(testFile); fileContents = fileContents.replace(/\r?\n|\r/g, ' '); fileContents = fileContents.replaceAll(' ', ''); if (fileContents.includes("loadFeature('")) { const firstPart = fileContents.split("loadFeature('")[1]; const fileName = firstPart.split("'")[0]; return fileName; } else if (fileContents.includes('loadFeature("')) { const firstPart = fileContents.split('loadFeature("')[1]; const fileName = firstPart.split('"')[0]; return fileName; } else { const featureFileGuess = testFile .replace('.steps.ts', '.feature') .replace('.steps.js', '.feature') .replace('.ts', '.feature') .replace('.js', '.feature') .replace('/step-definitions/', '/features/'); try { readFileDI(featureFileGuess); return featureFileGuess; } catch { throw new Error(`:'( Could not find the feature file mentioned in the test file. Make sure you include a loadFeature() call in your test file...`); } } }; const readFile = (file) => { const data = fs.readFileSync(file, { encoding: 'utf8', flag: 'r' }); return data; }; //# sourceMappingURL=Encoder.js.map