how to use rollup to parse cucumber feature files and backing step definition files - rollup

I have the following rollup plugin that will include .feature files
export const cucumberRollupPlugin: PluginImpl<CucumberOptions> = pluginOptions => {
let options: CucumberOptions = {
...{
include: '**/*.feature',
cwd: process.cwd(),
},
...pluginOptions,
};
let filter = createFilter(options);
let plugin: Plugin = {
name: 'bigtest-cucumber',
async transform(code, id) {
if (!filter(id)) {
return;
}
let parser = new GherkinParser({ code, uri: id, rootDir: options.cwd });
let result = await parser.parse();
let esm = dataToEsm(result, { namedExports: false });
// TODO: add sourcemap support
let transformResult: TransformResult = { code: esm };
return transformResult;
},
};
return plugin;
};
The problem I have is that to make the feature files work, there are step definition files that actually contain the functionality. So a feature file might look like this
Feature: Visit career guide page in career.guru99.com
Scenario: Visit career.guru99.com
Given: I browse to career.guru99.com
And a step definition file might look like this:
import { Given, When, Then from 'cucumber';
import assert from 'assert';
import{ driver } from '../support/web_driver';
Given(/^browse to web site "([^"]*)"$/, async function(url) {
return driver.get(url);
});
The problem I have with rollup is that there are no import statements for either the step definition files. The way cucumber-js works is that these files are found at runtime.
I think I need to generate an index.js that looks like this to cover the step definitions.
import from './step_definition_1';
import from './step_definition_2';
import from './step_definition_3';
Where would this fit in the rollup pipeline to generate this file so it can get pulled into the rollup pipeline.

You can use this.emitFile to manually process the .feature files and include them in the output. Call this.emitFile for each .feature file in the buildStart hook (each emitted file will get processed through the transform hook you wrote).
Here's an example that uses the globby package (which expands a glob to an array of file paths) to get the file path of each .feature file to pass to this.emitFile:
import globby from 'globby'
export const cucumberRollupPlugin: PluginImpl<CucumberOptions> = pluginOptions => {
let options: CucumberOptions = {
include: '**/*.feature',
cwd: process.cwd(),
...pluginOptions,
};
let filter = createFilter(options);
let plugin: Plugin = {
name: 'bigtest-cucumber',
async buildStart({ include, cwd }) {
const featureFilePaths = await globby(include, { cwd });
for (const featureFilePath of featureFilePaths) {
this.emitFile({
type: 'chunk',
id: featureFilePath
});
}
},
async transform(code, id) {
if (!filter(id)) {
return;
}
let parser = new GherkinParser({ code, uri: id, rootDir: options.cwd });
let result = await parser.parse();
let esm = dataToEsm(result, { namedExports: false });
// TODO: add sourcemap support
let transformResult: TransformResult = { code: esm };
return transformResult;
},
};
return plugin;
};
Let me know if you have any questions!

You need to use the this.emitFile method and befor that lookup the files via glob or anything else inside your plugin
{
plugins: [typescript(),{
name: "emit-additional-files",
async transform(code,id) {
//id === fileName
//code === fileContent
// inspect code or id here then use path.resolve() and fs.readdir to find additional files
this.emitFile()
}
}]
}

Related

How to add included Pug files to Vite module graph

I wrote a Rollup plugin to import Pug as an HTML string:
// Rollup plugin imported to Vite config
import { render } from 'pug';
export default function pug() {
return {
name: 'rollup-plugin-pug-html',
transform(src, id) {
if (id.endsWith('.pug')) {
const html = render(src, { filename: id });
const code = `export default ${JSON.stringify(html)};`;
return { code };
}
},
};
}
I'm using it in Vite to create templates for Vue components, as in this reduced example:
// ProofOfConceptSFC.vue
<script>
import { compile } from 'vue/dist/vue.esm-bundler.js';
import template from './template.pug';
export default {
render: compile(template)
};
</script>
The HMR is working great when I edit template.pug. The new template appears and the latest reactive values persist.
My problem is that template.pug may depend on other Pug files with include:
//- template.pug
include ./header.pug
p Hello {{ name }}
include ./footer.pug
The Vite server doesn't know about those files, and nothing happens if I touch them. Ideally I could invalidate template.pug when any Pug file is changed.
I'm guessing I want my plugin to update the ViteDevServer's server.moduleGraph. Is there a supported way to do that?
Huge thanks to the friendly Vite chat on Discord for setting me in the right direction.
The two keys I was missing:
Use Pug compile to create a render method that has render.dependencies, as done by Parcel
Use virtual import statements to attach the dependencies to the transform hook result, as done by vite-plugin-svelte.
Here is the working plugin:
import { compile } from 'pug';
export default function pluginPug() {
return {
name: 'vite-plugin-pug',
transform(src, id) {
if (id.endsWith('.pug')) {
const render = compile(src, { filename: id });
const html = render();
let code = '';
for (let dep of render.dependencies) {
code += `import ${JSON.stringify(dep)};\n`;
}
code += `export default ${JSON.stringify(html)};`;
return { code };
}
},
};
}

how to create pdf in react-native

I want to create pdf in my app in react-native for word to pdf or html to pdf converter.I have tried all possible react-native packages but none of them give me a desired result what should I do.
Are you using nodeJS on the server? If you are, send the html to the server and use this to convert the html to a pdf.
Have you Checked react-native-pdf-lib.
It is pretty good module.
Otherwise consider server side rendering
You can generate pdf file in react native with the npm package react-native-html-to-pdf.
Steps :
Create HTML template of your pdf file you want to generate.
Install the package and install pod in ios folder with pod install
Rebuild the app in android/ios.
import RNHTMLtoPDF from 'react-native-html-to-pdf';
const createPdf = async () => {
try {
let options = {
html: '<div>Hello pdf</div>',
fileName: 'demofile',
directory: 'Downloads',
base64: true,
};
let file = await RNHTMLtoPDF.convert(options);
console.log(file);
} catch (error) {
console.log(error);
}
}
Here is the more specific answer to your problem :
import RNHTMLtoPDF from 'react-native-html-to-pdf';
const createPdf = async () => {
let h1 = "Hey Heading 1";
let h2 = "heading 2";
let para = "Something description";
// use Backtick(`) to define html for your custom dynamic content
const pdf_html = `<div>
<h1>{h1}</h1>
<h1>{h2}</h1>
<h1>{para}</h1>
</div`;
try {
let options = {
html: pdf_html,
fileName: 'demofile',
directory: 'Downloads',
base64: true,
};
let file = await RNHTMLtoPDF.convert(options);
console.log(file);
} catch (error) {
console.log(error);
}
}

Having Vue Components as entry point instead of main.js

I'm working with a Java backend and Jersey and want to have the possibility to have different small page app.
My idea was to have a frontend module in which I would have a folder /apps.
The folder apps would then contain multiple vue components (that will be the main apps).
Another folder /component contains the different components that will be used in the different apps.
The idea is to have a webpack that would create one js file per vue app !
I know that Webpack is not specially designed to have multiple entrypoint / multiple outputs but does anyone have any idea how I could have multiple entry points being the different apps-file.vue and having multiple .js files as output ?
I had a similar problem and this answer pointed me in the right direction: I followed the Vue CLI docs to add a pages configuration option in a vue.config.js file.
After some experimentation, I got to a solution I was happy with. I saved it in repo that describes step-by-step how to create a multi-page Vue app from scratch.
https://github.com/chriscalo/vue-multipage
Some of the main things I was looking for:
Uses the Vue CLI, which means you can avoid most webpack
configuration headaches.
Doesn't force you to create an entry point .js file for each app /
page.
With this approach, you just place a bunch of .vue files in the src/pages directory and it generates a separate Vue app for each one. (You can pretty easily change that folder name from pages to apps if you would like.)
Autogenerate pages config and entry points
The crux of the solution is a script that creates a src/entry/bar/index.js entry point file for every src/pages/bar.vue file found and also generates a src/entry/pages.config.js file that you can import into your vue.config.js file like so:
const pagesConfig = require("./src/entry/pages.config.js");
module.exports = {
pages: pagesConfig,
};
Here's the script:
const path = require("path");
const glob = require("fast-glob");
const fse = require("fs-extra");
const R = require("ramda");
const { stripIndent } = require("common-tags");
const pathGlob = processRelativePath("../src/pages/**/*.vue");
const vuePagesPromise = glob(pathGlob);
console.log(`Generating entry points`);
// Step 1: compute specifications for work to be done
const pagesConfigPromise = vuePagesPromise.then(pages => {
return pages.map(page => {
const { dir, name } = path.parse(page);
const entryRoot = path.relative("src/pages", dir);
const entryName = (
split(entryRoot, path.sep)
).concat(
ensureEndsWith([name], "index")
).join(path.sep);
const entryFilePath = path.join(
processRelativePath("../src/entry"), `${entryName}.js`
);
const importPath = path.relative("src", page);
const entryFileContent = entryPointContent(importPath);
return {
source: page,
entryName,
entryFilePath,
entryFileContent,
};
});
});
// Step 2: clear entry folder
const entryFolderPath = processRelativePath("../src/entry");
fse.removeSync(entryFolderPath);
console.log(`Cleared ${entryFolderPath}`);
// Step 3: create a corresponding entry point file for each page
pagesConfigPromise.then(config => {
config.forEach(page => {
fse.outputFileSync(page.entryFilePath, page.entryFileContent);
console.log(`Created ${page.entryFilePath}`);
});
});
// Step 4: create a pages.config.js
// module.exports = {
// "index": 'src/pages/index.js',
// "login/index": "src/pages/login.js",
// "profile/index": "src/pages/profile/index.js",
// "foo/index": 'src/pages/foo.js',
// "bar/index": 'src/pages/bar/index.js',
// };
const pagesConfigPath = processRelativePath("../src/entry/pages.config.js");
pagesConfigPromise
.then(config => {
// transforms each into something like:
// { "login/index": "src/pages/login.js" }
return config.map(page => ({
[page.entryName]: page.entryFilePath,
}));
})
.then(R.mergeAll)
.then(pageConfigContent)
.then(content => fse.outputFileSync(pagesConfigPath, content))
.then(() => console.log(`Created ${pagesConfigPath}`));
function pageConfigContent(config) {
return stripIndent`
module.exports = ${JSON.stringify(config, null, 2)};
`;
}
function processRelativePath(p) {
const pathToThisDir = path.relative(process.cwd(), __dirname);
return path.join(pathToThisDir, p);
}
// fixes split() behavior for empty string ("")
function split(string, separator) {
if (string.length === 0) {
return [];
} else {
return string.split(separator);
}
}
function ensureEndsWith(array, item) {
if (array.slice(-1)[0] === item) {
return array;
} else {
return array.concat([item]);
}
}
function entryPointContent(importPath) {
return stripIndent`
import Vue from "vue";
import page from "#/${importPath}";
new Vue({
render: h => h(page),
}).$mount('#app');
`;
}

Nightwatch: Separate xpath into another files

How to separate xpath into separate file and use it in nightwatch automation testing?
EDIT:
I running a page object pattern example and found some errors.
Errors: TypeError: browser.page.url is not a function
Please help on this.
module.exports = {
url: 'http://localhost:63916/Login/Login',
elements: {
username: {
selector: '//*[#id="inputName"]',
locateStrategy: 'xpath'
}
}
};
module.exports = (function(settings) {
settings.test_workers = false;
return settings;
})(require('./nightwatch.json'));
//const data = require('./data.js')
module.exports = {
'Login' : function (browser) {
var page = browser.page.url();
page.navigate()
.setValue('#username', 'peter')
browser.end()
}
};
So assuming the page object is defined in pages directory. You need to change your nightwatch.conf.js like below
nightwatch.conf.js
module.exports = (function(settings) {
settings.test_workers = false;
settings.page_objects_path = "./pages";
return settings;
})(require('./nightwatch.json'));
Your pages has a file named main.js
pages/main.js
module.exports = {
url: 'https://gmail.com',
elements: {
username: {
selector: '//*[#id="identifierId"]',
locateStrategy: 'xpath'
},
next: {
selector: '#identifierNext span',
locateStrategy: 'css'
}
}
};
And then you test is like below
tests/test.main.js
module.exports = {
'Login' : function (browser) {
var page = browser.page.main();
page.navigate()
.setValue('#username', 'peterlalwani')
.click('#next')
.waitForElementNotPresent("#username", 10000);
browser.saveScreenshot("./so.png")
browser.end()
}
};
Now when you run it, it creates a so.png
I created a sample repo for you to clone and see the above
https://github.com/tarunlalwani/nightwatch-page-object-so.git
PS: It is important to note that var page = browser.page.main(); means it would load main.js from the pages folder here.

What is the best approach to test a HapiJS plugin, with Lab?

What is the best way to test a HapiJS plugin, for example one plugin that add routes and handlers.
Since I have to create an instance of Hapi.Server to run the plugins, should I define all the tests from the app's root, for all the plugins ?
or
should I manage to get THE instance of Hapi.Server in my plugin's local tests ?
If I go for the second option, my server will have registered all the plugins, including those that the plugin to be tested doesn't depends on.
What is the best way to approach this ?
Thanks in advance.
If you're using Glue (and I highly recommend it), you can create a manifest variable for each test (or group of tests) you want to execute. The manifest only needs to include plugins required for that test to execute properly.
And expose some sort of init function to actually start your server. Small example:
import Lab = require("lab");
import Code = require('code');
import Path = require('path');
import Server = require('../path/to/init/server');
export const lab = Lab.script();
const it = lab.it;
const describe = lab.describe;
const config = {...};
const internals = {
manifest: {
connections: [
{
host: 'localhost',
port: 0
}
],
registrations: [
{
plugin: {
register: '../http_routes',
options: config
}
},
{
plugin: {
register: '../business_plugin',
options: config
}
}
]
},
composeOptions: {
relativeTo: 'some_path'
}
};
describe('business plugin', function () {
it('should do some business', function (done) {
Server.init(internals.manifest, internals.composeOptions, function (err, server) {
// run your tests here
});
});
});
init function:
export const init = function (manifest: any, composeOptions: any, next: (err?: any, server?: Hapi.Server) => void) {
Glue.compose(manifest, composeOptions, function (err: any, server: Hapi.Server) {
if (err) {
return next(err);
}
server.start(function (err: any) {
return next(err, server);
});
});
};