11 |
12 |
--------------------------------------------------------------------------------
/js/data_generation/faker/fakerTestDataGenerator.js:
--------------------------------------------------------------------------------
1 | import { FakerCommand } from "./fakerCommand.js";
2 |
3 | export class FakerTestDataGenerator{
4 |
5 | constructor(aFaker){
6 | this.faker = aFaker;
7 | }
8 |
9 |
10 | generateFrom(aRule){
11 |
12 | const fakerCommand = new FakerCommand(aRule.ruleSpec);
13 | fakerCommand.parse();
14 | fakerCommand.compile(this.faker);
15 | return fakerCommand.execute(this.faker);
16 |
17 | }
18 | }
--------------------------------------------------------------------------------
/docs-src/src/pages/index.module.css:
--------------------------------------------------------------------------------
1 | /**
2 | * CSS files with the .module.css suffix will be treated as CSS modules
3 | * and scoped locally.
4 | */
5 |
6 | .heroBanner {
7 | padding: 4rem 0;
8 | text-align: center;
9 | position: relative;
10 | overflow: hidden;
11 | }
12 |
13 | @media screen and (max-width: 996px) {
14 | .heroBanner {
15 | padding: 2rem;
16 | }
17 | }
18 |
19 | .buttons {
20 | display: flex;
21 | align-items: center;
22 | justify-content: center;
23 | }
24 |
--------------------------------------------------------------------------------
/js/data_generation/ruleResponse.js:
--------------------------------------------------------------------------------
1 | export class RuleResponse{
2 |
3 | constructor(isError, errorMessage, data){
4 | this.isError = isError ? isError : false;
5 | this.errorMessage = errorMessage ? errorMessage : "";
6 | this.data = data ? data : "";
7 | }
8 | }
9 |
10 | export function errorResponse(errorMessage){
11 | return new RuleResponse(true, errorMessage, "");
12 | }
13 |
14 | export function dataResponse(data){
15 | return new RuleResponse(false, "", data);
16 | }
--------------------------------------------------------------------------------
/js/data_generation/testDataRule.js:
--------------------------------------------------------------------------------
1 | class TestDataRule{
2 |
3 | constructor(aName, aRule="") {
4 | this.name = aName;
5 | this.ruleSpec = aRule;
6 | // we don't know what the command is until we compile it
7 | this.fakerCommand = "";
8 | this.type=""; // by default no type,
9 | // can be assigned 'regex' or 'faker' or 'literal'
10 | // in future more types can be created
11 | }
12 |
13 | }
14 |
15 | export {TestDataRule};
--------------------------------------------------------------------------------
/js/data_generation/regex/regexTestDataGenerator.js:
--------------------------------------------------------------------------------
1 | import { dataResponse, errorResponse } from "../ruleResponse.js";
2 |
3 | export class RegexTestDataGenerator{
4 |
5 | constructor(aRandExp){
6 | this.RandExp = aRandExp;
7 | }
8 |
9 |
10 | generateFrom(aRule){
11 | try{
12 | const data = new this.RandExp(aRule.ruleSpec).gen();
13 | return dataResponse(data);
14 | }catch(e){
15 | return errorResponse("Regex Generation Error " + e);
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/cli/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies (bun install)
2 | node_modules
3 |
4 | # output
5 | out
6 | dist
7 | *.tgz
8 |
9 | # code coverage
10 | coverage
11 | *.lcov
12 |
13 | # logs
14 | logs
15 | _.log
16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17 |
18 | # dotenv environment variable files
19 | .env
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .env.local
24 |
25 | # caches
26 | .eslintcache
27 | .cache
28 | *.tsbuildinfo
29 |
30 | # IntelliJ based IDEs
31 | .idea
32 |
33 | # Finder (MacOS) folder config
34 | .DS_Store
35 |
--------------------------------------------------------------------------------
/cli/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "anywaydata",
3 | "module": "index.ts",
4 | "type": "module",
5 | "private": true,
6 | "devDependencies": {
7 | "@types/bun": "latest",
8 | "@types/yargs": "^17.0.33"
9 | },
10 | "peerDependencies": {
11 | "typescript": "^5"
12 | },
13 | "bin": {
14 | "anywaydata": "./index.ts"
15 | },
16 | "dependencies": {
17 | "@faker-js/faker": "^9.7.0",
18 | "@types/papaparse": "^5.3.15",
19 | "papaparse": "^5.5.2",
20 | "randexp": "^0.5.3",
21 | "yargs": "^17.7.2"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/js/gui_components/download.js:
--------------------------------------------------------------------------------
1 | class Download {
2 |
3 | constructor(filename) {
4 | this.filename = filename;
5 | }
6 |
7 | downloadFile(text) {
8 | var element = document.createElement('a');
9 | element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
10 | element.setAttribute('download', this.filename);
11 |
12 | element.style.display = 'none';
13 | document.body.appendChild(element);
14 |
15 | element.click();
16 |
17 | document.body.removeChild(element);
18 | }
19 | }
20 |
21 | export {Download}
--------------------------------------------------------------------------------
/js/utils/papawrappa.js:
--------------------------------------------------------------------------------
1 | // since papaparse is global
2 | // a class allows us to use a mock/stub during testing instantiated to a node version
3 | // or mock it completely
4 | // and at run time, instantiate using the window.Papa version
5 | export class PapaWrappa{
6 |
7 | parse(value, options){
8 | if(options){
9 | return Papa.parse(value, options);
10 | }
11 | return Papa.parse(value);
12 | }
13 |
14 | unparse(value, options){
15 | if(options){
16 | return Papa.unparse(value, options);
17 | }
18 | return Papa.unparse(value);
19 | }
20 | }
--------------------------------------------------------------------------------
/js/data_generation/testDataRules.js:
--------------------------------------------------------------------------------
1 | import {TestDataRule} from "./testDataRule.js";
2 |
3 | class TestDataRules{
4 |
5 | constructor() {
6 | this.rules = [];
7 | this.errors = [];
8 | }
9 |
10 | getRules(){
11 | return JSON.parse(JSON.stringify(this.rules));
12 | }
13 |
14 | getRule(aName){
15 | const retRules = this.rules.filter(name => name==aName.toLowerCase().trim())
16 | return retRules[0];
17 | }
18 |
19 | addRule(aName, aRule){
20 | this.rules.push(new TestDataRule(aName.trim(), aRule));
21 | }
22 | }
23 |
24 |
25 |
26 | export {TestDataRules};
--------------------------------------------------------------------------------
/tests/data_formats/delimiter-convertor.test.js:
--------------------------------------------------------------------------------
1 | import Papa from "papaparse";
2 |
3 | class PapaWrappa{
4 |
5 | parse(value, options){
6 | if(options){
7 | return Papa.parse(value, options);
8 | }
9 | return Papa.parse(value);
10 | }
11 |
12 | unparse(value, options){
13 | if(options){
14 | return Papa.unparse(value, options);
15 | }
16 | return Papa.unparse(value);
17 | }
18 | }
19 |
20 | describe("Can test stuff",()=>{
21 |
22 | test('can assert stuff', () => {
23 | // TODO: now that we can stub Papaparse we can test the convertor
24 | })
25 | })
--------------------------------------------------------------------------------
/images/circle-question-solid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/js/data_generation/regex/regexTestDataRuleValidator.js:
--------------------------------------------------------------------------------
1 | export class RegexTestDataRuleValidator{
2 |
3 | constructor(aRandExp) {
4 | this.RandExp=aRandExp;
5 | this.validationError = "";
6 | }
7 |
8 | validate(aTestDataRule){
9 | this.validationError="";
10 |
11 | try{
12 | new this.RandExp(aTestDataRule.ruleSpec).gen();
13 | return true;
14 | }catch(err){
15 | this.validationError = err;
16 | return false;
17 | }
18 | }
19 |
20 | isValid(){
21 | return this.validationError.length == 0;
22 | }
23 |
24 | getValidationError(){
25 | return this.validationError;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-18-options.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: html-table-output-options
3 | title: HTML Table Output Options
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-18T12:00
7 | ---
8 |
9 | Can now control HTML output.
10 |
11 |
12 |
13 | ## HTML Options
14 |
15 | I've added options to the HTML output now so it is possible to:
16 |
17 | - compact the HTML
18 | - pretty print html to add indentation
19 | - control the pretty print indentation characters
20 | - add tbody and thead elements
21 |
22 | ## Refactoring and Testing
23 |
24 | In addition, there have been a tonne of internal refactorings and bug fixes to make the code more streamlined and make it easier to add new export and import options in the future.
--------------------------------------------------------------------------------
/docs-src/blog/2022-05-30-initial-blog-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: initial-blog-post
3 | title: Added Documentation
4 | authors: alan
5 | tags: [release]
6 | date: 2022-05-30T10:00
7 | ---
8 |
9 | Initial Release of docs.
10 |
11 |
12 |
13 | I've been busy refactoring the code of the [Data Table Editor](https://github.com/eviltester/grid-table-editor/)
14 |
15 | - to make it more modular
16 | - add unit tests
17 | - add documentation
18 |
19 | This should make the application more robust and easier to use.
20 |
21 | It is still hosted on Gitpages, but if more people use the app then I'll setup a domain for it.
22 |
23 | Currently web traffic is tracked using plausible.io and no data is passed to the server other than counts of page views.
--------------------------------------------------------------------------------
/cli/src/outputData.js:
--------------------------------------------------------------------------------
1 | function convertDataToArrayOfStrings(data){
2 |
3 | // data is row of values where the first row is the headers
4 | // v9 of Faker can return an object
5 | // then it might be an object with "data" or just a String
6 | // row.map(it => it["data"] ? it.data : it)
7 | // but we only return the data or **ERROR** from the faker generation
8 |
9 | var header = true;
10 |
11 | var addRows = [];
12 |
13 | data.forEach(row => {
14 | if (header) {
15 | addRows.push(row)
16 | header = false;
17 | } else {
18 | addRows.push(row.map(it => it["data"] ? it.data : it));
19 | }
20 | });
21 |
22 | return addRows;
23 | }
24 |
25 | export {convertDataToArrayOfStrings}
--------------------------------------------------------------------------------
/tests/data_generation/unit/faker/parsingUtils.test.js:
--------------------------------------------------------------------------------
1 | import {removeStartAndEnd} from '../../../../js/data_generation/faker/parsingUtils.js';
2 |
3 | describe("removeStartAndEnd is a function to help with parsing",()=>{
4 |
5 | test('can find content in single quotes', () => {
6 | const result = removeStartAndEnd("'","'","'bob'");
7 | expect(result).toBe("bob");
8 | });
9 | test('leave alone if not fully matching', () => {
10 | const result = removeStartAndEnd("'",")","'bob'");
11 | expect(result).toBe("'bob'");
12 | });
13 | test('only removes the first and last character', () => {
14 | const result = removeStartAndEnd(" '","' "," 'bob' ");
15 | expect(result).toBe("'bob'");
16 | });
17 | });
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-20-overview-video.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: overview-video-faker
3 | title: Overview video of Faker Functionality
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-21T12:00
7 | ---
8 |
9 | import ReactPlayer from 'react-player'
10 |
11 | Released an overview video of Faker Functionality.
12 |
13 |
14 |
15 | I added a new page to the documentation with some Faker tips. Proper documentation will be coming soon.
16 |
17 | - https://anywaydata.com/docs/videos/faker-test-data
18 |
19 | This explains how to use Faker, and the approach I use when creating test data.
20 |
21 | ## Overview Video
22 |
23 | The overview video is also embedded below:
24 |
25 |
26 |
--------------------------------------------------------------------------------
/cli/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // Environment setup & latest features
4 | "lib": ["ESNext"],
5 | "target": "ESNext",
6 | "module": "ESNext",
7 | "moduleDetection": "force",
8 | "jsx": "react-jsx",
9 | "allowJs": true,
10 |
11 | // Bundler mode
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "verbatimModuleSyntax": true,
15 | "noEmit": true,
16 |
17 | // Best practices
18 | "strict": true,
19 | "skipLibCheck": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "noUncheckedIndexedAccess": true,
22 |
23 | // Some stricter flags (disabled by default)
24 | "noUnusedLocals": false,
25 | "noUnusedParameters": false,
26 | "noPropertyAccessFromIndexSignature": false
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/js/gui_components/options_panels/options-javascript-panel.js:
--------------------------------------------------------------------------------
1 | import {JsonOptionsPanel} from './options-json-panel.js';
2 |
3 | class JavascriptOptionsPanel{
4 |
5 | constructor(parentElement) {
6 | this.parent = parentElement;
7 | this.panel = new JsonOptionsPanel(parentElement, "javascript-options");
8 | }
9 |
10 | addToGui(){
11 | this.panel.addToGui();
12 | }
13 |
14 | setApplyCallback(callbackFunc){
15 | this.panel.setApplyCallback( function (){
16 | callbackFunc(this.getOptionsFromGui())
17 | }.bind(this));
18 |
19 | }
20 |
21 |
22 | getOptionsFromGui(){
23 | return this.panel.getOptionsFromGui();
24 | }
25 |
26 | setFromOptions(mainOptions){
27 | this.panel.setFromOptions(mainOptions);
28 | }
29 |
30 | }
31 |
32 | export {JavascriptOptionsPanel};
--------------------------------------------------------------------------------
/docs-src/docs/020-editing-data/030-text-editing.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | ---
4 |
5 | # Text Editing
6 |
7 | The data in the text area is configured by choosing a specific tab format e.g. selecting `Markdown` will show the grid data in Markdown format, selecting `CSV` will show the grid data as Comma Separated Values.
8 |
9 | It is possible to edit data in this text area.
10 |
11 | When all edits are complete, click the `^ Set Grid From Text ^` button and the contents of the text area will be loaded into the data grid.
12 |
13 | > *Warning* The text area defaults to showing data from the Data Grid. If you edit the data in the text area and change tabs then any changes you made will be lost because the new tab will show the data in the grid formatted to the chosen format. If you want to keep any changes then `Set the Grid From Text`.
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs-src/sidebars.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Creating a sidebar enables you to:
3 | - create an ordered group of docs
4 | - render a sidebar for each doc of that group
5 | - provide next/previous navigation
6 |
7 | The sidebars can be generated from the filesystem, or explicitly defined here.
8 |
9 | Create as many sidebars as you want.
10 | */
11 |
12 | // @ts-check
13 |
14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
15 | const sidebars = {
16 |
17 | // By default, Docusaurus generates a sidebar from the docs folder structure
18 | docs: [{type: 'autogenerated', dirName: '.'}],
19 |
20 | // But you can create a sidebar manually
21 | /*
22 | tutorialSidebar: [
23 | {
24 | type: 'category',
25 | label: 'Tutorial',
26 | items: ['hello'],
27 | },
28 | ],
29 | */
30 | };
31 |
32 | module.exports = sidebars;
33 |
--------------------------------------------------------------------------------
/js/data_formats/file-types.js:
--------------------------------------------------------------------------------
1 | // TODO : this should be wrapped into the importer and exporter so an exporter returns filetype
2 | // TODO : there should be a default file name as well as extension e.g. asciitable.txt to help disctinguish from dsv.txt
3 | // TODO : roll this into the convertors
4 | const fileTypes ={};
5 | fileTypes["csv"] = {type: "csv", fileExtension: ".csv"};
6 | fileTypes["dsv"] = {type: "dsv", fileExtension: ".txt"};
7 | fileTypes["markdown"] = {type: "markdown", fileExtension: ".md"};
8 | fileTypes["json"] = {type: "json", fileExtension: ".json"};
9 | fileTypes["javascript"] = {type: "javascript", fileExtension: ".js"};
10 | fileTypes["gherkin"] = {type: "gherkin", fileExtension: ".gherkin"};
11 | fileTypes["html"] = {type: "html", fileExtension: ".html"};
12 | fileTypes["asciitable"] = {type: "asciitable", fileExtension: ".txt"};
13 |
14 |
15 | export {fileTypes};
--------------------------------------------------------------------------------
/tests/data_generation/unit/rulesParser.test.js:
--------------------------------------------------------------------------------
1 | import {RulesParser} from '../../../js/data_generation/rulesParser.js';
2 | import { faker } from '@faker-js/faker';
3 |
4 | const RandExp = require('randexp');
5 |
6 | describe("RulesParser parses a block of text to return a collection of rules",()=>{
7 |
8 | test('can parse a valid two line string into rules', () => {
9 | const inputText =
10 | `Name
11 | person.fullName`
12 |
13 | const parser = new RulesParser(faker, RandExp);
14 | parser.parseText(inputText);
15 |
16 | expect(parser.isValid()).toBe(true);
17 |
18 | expect(parser.testDataRules.rules[0].name).toBe("Name");
19 | // current parser does not parse the type, type is assigned during compilation
20 | expect(parser.testDataRules.rules[0].type).toBe("");
21 | expect(parser.testDataRules.rules[0].ruleSpec).toBe("person.fullName");
22 |
23 | });
24 |
25 | });
26 |
--------------------------------------------------------------------------------
/docs-src/docs/020-editing-data/020-import-from-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: "Import From File"
4 | description: "Data can be imported from various file types."
5 | ---
6 |
7 | # Importing from Files
8 |
9 | The set of tabs `Markdown | CSV | JSON` etc. configure the current import and export format.
10 |
11 | To import from a particular file format:
12 |
13 | - choose the tab for a particular format e.g. `Markdown`
14 | - click the `Choose file` button
15 | - choose a Markdown file to load
16 |
17 | The data will then be loaded into the Data Grid for editing via the grid, and shown in the text area for text editing.
18 |
19 | ## Drag And Drop Import
20 |
21 | Once a file format tab has been selected it is also possible to drag and drop a file on to the GUI to start an import.
22 |
23 | e.g. if `CSV` tab is chosen then the Drop Zone will read `[Drag And Drop .csv File Here]`, allowing you to drag and drop a file to this area to trigger an import.
--------------------------------------------------------------------------------
/docs-src/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ yarn
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ yarn start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | Using SSH:
30 |
31 | ```
32 | $ USE_SSH=true yarn deploy
33 | ```
34 |
35 | Not using SSH:
36 |
37 | ```
38 | $ GIT_USER= yarn deploy
39 | ```
40 |
41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
42 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ "master" ]
9 | pull_request:
10 | branches: [ "master" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [18.x]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v3
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | cache: 'npm'
29 | - run: npm ci
30 | - run: npm run build --if-present
31 | - run: npm run testverbose
32 |
--------------------------------------------------------------------------------
/js/utils/number-convertor.js:
--------------------------------------------------------------------------------
1 | function isNumber(str) {
2 | var pattern = /^\d+$/;
3 | return pattern.test(str);
4 | }
5 |
6 | function getNumberArrayFrom(aString){
7 | let ret = [];
8 |
9 | if(aString===undefined || aString===null){
10 | return ret;
11 | }
12 |
13 | let trimmed = aString.trim();
14 | if(trimmed.length===0){
15 | return ret;
16 | }
17 |
18 | let notnumbers = /[^0-9]/gi;
19 | let toParse = trimmed.replace(notnumbers, ' ');
20 | toParse = toParse.trim();
21 |
22 | let parsed = toParse.split(" ");
23 |
24 | for(const item of parsed){
25 | if(item===undefined || item===null || item.trim().length===0){
26 | // ignore it
27 | }else{
28 |
29 | if(isNumber(item)){
30 | const parsedNum = parseInt(item);
31 | if (!isNaN(parsedNum)) {
32 | ret.push(parsedNum)
33 | }
34 | }
35 | }
36 | }
37 |
38 | return ret;
39 | }
40 |
41 | export {getNumberArrayFrom, isNumber}
--------------------------------------------------------------------------------
/docs-src/docs/020-editing-data/040-exporting-data.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | title: "Exporting Data"
4 | ---
5 |
6 | # Exporting Data
7 |
8 | Exporting Data is driven by the data in the Data Grid.
9 |
10 | The Exported Data Format depends on the tab which has been selected e.g. `Markdown` or `CSV`.
11 |
12 | ## Exporting to File
13 |
14 | When a tab has been selected the download button will change to match the format e.g. `.md download` or `.csv download`.
15 |
16 | Pressing the download button will start a conversion of the data in the grid to the specific format.
17 |
18 | ## Exporting to Clipboard
19 |
20 | To export to clipboard either:
21 |
22 | - select the data in the text area and use normal copy keyboard shortcuts or the right click context menu.
23 | - press the `[Copy]` button to the right of the clipboard
24 |
25 | > *NOTE:* if the data in the grid has been edited ensure you press the `v Set Text From Grid v` button before copying to the clipboard to make sure the data in the clipboard matches the data in the grid.
26 |
--------------------------------------------------------------------------------
/js/data_generation/faker/fakerCommandRunner.js:
--------------------------------------------------------------------------------
1 | export function runFakerCommand(thisCommand, theseArguments, usingFaker){
2 |
3 | const executionResult = {isError: true, errorMessage: "Not Executed", data: ""};
4 |
5 | const useArguments = theseArguments ? theseArguments : "()"
6 |
7 | var fakerPrefix="this.";
8 |
9 | const commandToRun = "return "+ fakerPrefix + thisCommand + useArguments;
10 | try{
11 | executionResult.isError= false;
12 | executionResult.errorMessage="";
13 | executionResult.data = Function(commandToRun).bind(usingFaker)();
14 | return executionResult;
15 | }catch(e){
16 | // console.log(commandToRun);
17 | // console.log(e);
18 | executionResult.isError= true;
19 | executionResult.errorMessage="Error running Commmand " + thisCommand + useArguments + " ERR: " + e;
20 | executionResult.data = "";
21 | return executionResult;
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "pwa-node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "skipFiles": [
12 | "/**"
13 | ],
14 | "program": "${workspaceFolder}/index.html"
15 | },
16 | {
17 | "name": "Debug Jest Tests",
18 | "type": "node",
19 | "request": "launch",
20 | "runtimeArgs": [
21 | "--inspect-brk",
22 | "${workspaceRoot}/node_modules/.bin/jest",
23 | "--runInBand"
24 | ],
25 | "console": "integratedTerminal",
26 | "internalConsoleOptions": "neverOpen",
27 | "port": 9229
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/js/data_formats/csv-convertor.js:
--------------------------------------------------------------------------------
1 | import { DelimiterConvertor } from "./delimiter-convertor.js";
2 | import { DelimiterOptions } from "./delimiter-options.js";
3 |
4 | export class CsvConvertor {
5 |
6 | constructor(params) {
7 | this.options = new DelimiterOptions(",");
8 | this.delegateTo = new DelimiterConvertor(this.options);
9 |
10 | if(params!==undefined){
11 | this.setOptions(params);
12 | }
13 | }
14 |
15 | setOptions(delimiterOptions){
16 | this.options.mergeOptions(delimiterOptions);
17 | this.options.delimiter = ",";
18 | this.delegateTo = new DelimiterConvertor(this.options);
19 | this.delegateTo.setPapaParse(this.papaparse);
20 | }
21 |
22 | setPapaParse(papaparse){
23 | this.papaparse=papaparse;
24 | this.delegateTo.setPapaParse(papaparse);
25 | }
26 |
27 | fromDataTable(dataTable){
28 | return this.delegateTo.fromDataTable(dataTable);
29 | }
30 |
31 | toDataTable(textToImport){
32 | return this.delegateTo.toDataTable(textToImport);
33 | }
34 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Alan Richardson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs-src/src/css/custom.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Any CSS included here will be global. The classic template
3 | * bundles Infima by default. Infima is a CSS framework designed to
4 | * work well for content-centric websites.
5 | */
6 |
7 | /* You can override the default Infima variables here. */
8 | :root {
9 | --ifm-color-primary: #2e8555;
10 | --ifm-color-primary-dark: #29784c;
11 | --ifm-color-primary-darker: #277148;
12 | --ifm-color-primary-darkest: #205d3b;
13 | --ifm-color-primary-light: #33925d;
14 | --ifm-color-primary-lighter: #359962;
15 | --ifm-color-primary-lightest: #3cad6e;
16 | --ifm-code-font-size: 95%;
17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
18 | }
19 |
20 | /* For readability concerns, you should choose a lighter palette in dark mode. */
21 | [data-theme='dark'] {
22 | --ifm-color-primary: #25c2a0;
23 | --ifm-color-primary-dark: #21af90;
24 | --ifm-color-primary-darker: #1fa588;
25 | --ifm-color-primary-darkest: #1a8870;
26 | --ifm-color-primary-light: #29d5b0;
27 | --ifm-color-primary-lighter: #32d8b4;
28 | --ifm-color-primary-lightest: #4fddbf;
29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
30 | }
31 |
--------------------------------------------------------------------------------
/tests/data_generation/unit/testDataRules.test.js:
--------------------------------------------------------------------------------
1 | import {TestDataRule} from '../../../js/data_generation/testDataRule.js';
2 |
3 | // to get import and export working from tests
4 | // https://stackoverflow.com/questions/35756479/does-jest-support-es6-import-export
5 |
6 | describe("TestDataRule is the simple type that underpins the rule processing",()=>{
7 |
8 | test('can instantiate a TestDataRule with just a name', () => {
9 | const testDataRule = new TestDataRule("just the name");
10 | expect(testDataRule.name).toBe("just the name");
11 | expect(testDataRule.ruleSpec).toBe("");
12 | });
13 |
14 | test('can instantiate a TestDataRule with a rule too', () => {
15 | const testDataRule = new TestDataRule("the name","[A-Z]");
16 | expect(testDataRule.name).toBe("the name");
17 | expect(testDataRule.ruleSpec).toBe("[A-Z]");
18 | });
19 |
20 | test('can instantiate a TestDataRule with an empty constructor but it will not help much', () => {
21 | const testDataRule = new TestDataRule();
22 | expect(testDataRule.name).toBeUndefined();
23 | expect(testDataRule.ruleSpec).toBe("");
24 | });
25 |
26 | });
27 |
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/csv/options.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: "CSV Options"
4 | description: "Options available for converting to CSV in AnyWayData.com"
5 | ---
6 |
7 | The configuration options for CSV are listed below.
8 |
9 | ## Use Quotes
10 |
11 | The `Use Quotes` options allows you to configure when quotes are added to the values:
12 |
13 | - when checked, then every value in the CSV file will be wrapped with quotes
14 | - when unchecked, the only values containing quotes or commas will be wrapped with quotes
15 |
16 | ## Use Header
17 |
18 | `Use Header` configures if the row header will be added to the first line of the CSV file or not.
19 |
20 | ## Quote Char
21 |
22 | The `Quote Char` option configures the quote character to use.
23 |
24 | By default this will be `"` but some tools may prefer `'`, and if so you can reconfigure the output.
25 |
26 | ## Escape Char
27 |
28 | The `Escape Char` option configures the character that will be used in front of the `Quote Char` when the `Quote Char` is present in the value.
29 |
30 | Most of the time this will be the same as the `Quote Char` itself, but you may need to configure it to `\` or some other value for other tools.
--------------------------------------------------------------------------------
/js/data_formats/delimiter-options.js:
--------------------------------------------------------------------------------
1 | class DelimiterOptions{
2 |
3 | constructor(delimiter){
4 | this.options = {
5 | quotes: true, //or array of booleans
6 | quoteChar: '"',
7 | escapeChar: '"',
8 | delimiter: "\"",
9 | header: true,
10 | newline: "\n",
11 | skipEmptyLines: false, //other option is 'greedy', meaning skip delimiters, quotes, and whitespace.
12 | columns: null //or array of strings
13 | }
14 |
15 | if(delimiter!==undefined){
16 | this.options.delimiter = delimiter;
17 | }
18 |
19 | this.headers = [];
20 | }
21 |
22 | mergeOptions(delimiterOptions){
23 |
24 | if(delimiterOptions.options){
25 | this.options = {...this.options, ...delimiterOptions.options}
26 | }else{
27 | this.options = {...this.options, ...delimiterOptions}
28 | }
29 |
30 | if(delimiterOptions.hasOwnProperty("headers")){
31 | this.headers = delimiterOptions.headers.map(header => header);
32 | }else{
33 | this.headers = [];
34 | }
35 | }
36 |
37 | }
38 |
39 | export {DelimiterOptions}
--------------------------------------------------------------------------------
/tests/utils/debouncer.test.js:
--------------------------------------------------------------------------------
1 | import {Debouncer} from '../../js/utils/debouncer.js';
2 |
3 | describe("Debouncer will call a function after a delay",()=>{
4 |
5 | function delay(time) {
6 | return new Promise(resolve => setTimeout(resolve, time));
7 | }
8 |
9 | test('will only call debouncer once', async () => {
10 |
11 | let countOfCalls = 0;
12 |
13 | function makeCall(){
14 | countOfCalls++;
15 | }
16 |
17 | let debouncer = new Debouncer();
18 |
19 | for(let x=0;x<100;x++){
20 | debouncer.debounce("makecall", makeCall, 50);
21 | }
22 |
23 | await delay(200);
24 | expect(countOfCalls).toBe(1);
25 | });
26 |
27 | test('can clear debounce and it will not fire', async () => {
28 |
29 | let countOfCalls = 0;
30 |
31 | function makeCall(){
32 | countOfCalls++;
33 | }
34 |
35 | let debouncer = new Debouncer();
36 |
37 | for(let x=0;x<100;x++){
38 | debouncer.debounce("makecall", makeCall, 50);
39 | }
40 |
41 | debouncer.clear("makecall");
42 |
43 | await delay(200);
44 | expect(countOfCalls).toBe(0);
45 | });
46 | });
--------------------------------------------------------------------------------
/docs-src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "anywaydata": "docusaurus build --out-dir ../build",
10 | "swizzle": "docusaurus swizzle",
11 | "deploy": "docusaurus deploy",
12 | "clear": "docusaurus clear",
13 | "serve": "docusaurus serve",
14 | "write-translations": "docusaurus write-translations",
15 | "write-heading-ids": "docusaurus write-heading-ids"
16 | },
17 | "dependencies": {
18 | "@docusaurus/core": "3.7.0",
19 | "@docusaurus/preset-classic": "3.7.0",
20 | "@mdx-js/react": "^3.0.0",
21 | "prism-react-renderer": "^2.1.0",
22 | "react": "^18.2.0",
23 | "react-dom": "^18.2.0",
24 | "react-player": "^2.16.0"
25 | },
26 | "devDependencies": {
27 | "@docusaurus/module-type-aliases": "3.7.0"
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.5%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-19-gherkin-options.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: gherkin-output-options
3 | title: Gherkin Table Output Options
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-19T12:00
7 | ---
8 |
9 | Can now control Gherkin output.
10 |
11 |
12 |
13 | ## Gherkin Options
14 |
15 | I've added options to the Gherkin output now so it is possible to:
16 |
17 | - pretty print gherkin to pad out the cell widths
18 | - indent the table from the left
19 | - add in cell padding to the left, right or both sides
20 | - hide or show the heading
21 |
22 | ## Gherkin Data Tables
23 |
24 | Gherkin is a literate Executable requirements format used in tools such as Cucumber.
25 |
26 | The Gherkin syntax supports table format to make it easier to pass in a set of data to work from when executing the requirement spec.
27 |
28 | Since these are designed for humans they need to be readable so adding spaces and indenting to aid readability is important, hence the options in Any Way Data.
29 |
30 | e.g. an example Gherkin Data Table
31 |
32 | ```
33 | Given the following user details are present:
34 | | name | email |
35 | | Bob | bob@bob.bob |
36 | ```
37 |
38 | Tables are described in the [Cucumber documentation](https://cucumber.io/docs/gherkin/reference/#data-tables).
--------------------------------------------------------------------------------
/docs-src/blog/2025-04-26-update-faker-create-cli.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: cli-and-faker-9
3 | title: Updated Faker and added prototype CLI
4 | authors: alan
5 | tags: [release]
6 | date: 2025-04-26T14:55
7 | ---
8 |
9 | We have updated the version of Faker used for Test Data Generation and added a basic CLI.
10 |
11 |
12 |
13 | ## Faker
14 |
15 | [Faker](https://fakerjs.dev/guide/) is the test data generation library used by AnyWayData.
16 |
17 | We have just updated the version in use to v9.7 so now the docs on the Faker site for API will match the test data generation here.
18 |
19 | ## CLI
20 |
21 | We have also created prototype CLI of anwaydata to generate CSV files from the same data spec templates used in the UI.
22 |
23 | So if you want to generate very large amounts of data then you can do so from the CLI rather than the web UI.
24 |
25 | CLI binaries are available from:
26 |
27 | [github.com/eviltester/grid-table-editor/releases](https://github.com/eviltester/grid-table-editor/releases)
28 |
29 | Docs can be found here:
30 |
31 | [github.com/eviltester/grid-table-editor/tree/v1.0.1/cli](https://github.com/eviltester/grid-table-editor/tree/v1.0.1/cli)
32 |
33 | The CLI is built using bun so if you have bun installed you can run the CLI locally after cloning the repo.
34 |
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/ascii-tables/010-ascii-tables.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "Ascii Tables"
4 | description: "Ascii Tables are often used in program source code, text files, chat messages or when sending emails, they are an output format rather than an input format."
5 | ---
6 |
7 | ## What are Ascii Tables
8 |
9 | Ascii Tables are a traditional way of representing data for Text Files or from Typewriters or early printers.
10 |
11 | They have remained popular because they can be embedded in Program code in comments to help describe data.
12 |
13 | e.g.
14 |
15 | ```
16 | +---------------+---------------+---------------+------------+-------------+
17 | | Country | Population | Yearly Change | Net Change | World Share |
18 | +---------------+---------------+---------------+------------+-------------+
19 | | China | 1,439,323,776 | 0.39 % | 5,540,090 | 18.47 % |
20 | | India | 1,380,004,385 | 0.99 % | 13,586,631 | 17.70 % |
21 | | United States | 331,002,651 | 0.59 % | 1,937,734 | 4.25 % |
22 | +---------------+---------------+---------------+------------+-------------+
23 | ```
24 |
25 | This is often more readable than an equivalent Markdown table because it is formatted purely for output and for readability.
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/html/options.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: "HTML Table Options"
4 | description: "Options available for generating HTML Tables: Pretty Printing, compact output, adding thead and tbody."
5 | ---
6 |
7 | The options for HTML are all output options.
8 |
9 | ## Compact
10 |
11 | The `Compact` checkbox allows you to remove all new lines from the generated HTML to create a very compact representation of the HTML Table.
12 |
13 | ## Pretty Print
14 |
15 | Pretty Print will indent the HTML lines using the `Delimiter` option as the first character in the line.
16 |
17 | ## Delimiter
18 |
19 | `Delimiter` allows you to choose the spacing at the start of a line for pretty printing.
20 |
21 | - Tab
22 | - Space
23 | - Custom Value
24 |
25 | When `Custom Value` is chosen, the string entered in the `Custom` field is used at the start of a line.
26 |
27 | ## Custom
28 |
29 | The `Custom` text entry is the string added at the front of each HTML output line e.g. to add three spaces instead of one ` `.
30 |
31 | This is only used when `Pretty Print` is selected and the `Delimiter` is `Custom Value`.
32 |
33 | ## Add ``
34 |
35 | This will add the `thead` element to the output as the parent for the header row.
36 |
37 | ## Add ``
38 |
39 | This will add the `tbody` element to the output as the parent for the main table data rows.
40 |
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-27-added-html-tables-help.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: docs-html-tables
3 | title: Added Documentation for HTML Tables
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-27T08:00
7 | ---
8 |
9 | We now have documentation for [HTML Tables](/docs/data-formats/html/html-tables) and [Options](/docs/data-formats/html/options)
10 |
11 |
12 |
13 | ## HTML Tables
14 |
15 | HTML Tables are the default tabular representation on web pages.
16 |
17 | It would be nice if Web Pages offered the option to 'right click' and save tables in different data formats, but they don't.
18 |
19 | With AnyWayData.com you can copy in the HTML for any table, edit in the data grid and convert to any of the supported formats e.g.
20 |
21 | - convert HTML Table to CSV
22 | - convert HTML Table to Markdown
23 | - convert HTML Table to Ascii Table
24 | - convert HTML Table to Json
25 | - etc.
26 |
27 | ## Tips
28 |
29 | Using the browser dev tools it is possible to right click the table in the web page, inspect the element and 'copy outer html' to get the HTML into the clipboard, or 'edit as html' and copy and paste.
30 |
31 | Any HTML table that can be rendered, can be imported, but:
32 |
33 | - styling is not carried through because we want to work with the raw data, not the styles
34 | - row spanning and column spanning are not supported so you would see 'undefined' in the data grid for those cell values
35 |
36 |
--------------------------------------------------------------------------------
/docs-src/blog/2022-05-30-anywaydata.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: now-at-anywaydata-com
3 | title: Moved to AnyWayData.com
4 | authors: alan
5 | tags: [release]
6 | date: 2022-05-30T15:00
7 | ---
8 |
9 | The [deployed version of the application](https://anywaydata.com/app.html) has moved to it's own domain of [anywaydata.com](https://anywaydata.com).
10 |
11 |
12 |
13 | Since I've started adding documentation, creating more tests and refactoring the code I decided to move the app to a hosting server and give it a custom domain.
14 |
15 | After much searching and head scratching to see what available domains were suitable, I stumbled on the concept of "Any Way".
16 |
17 | You can:
18 |
19 | - edit the data "Any Way" in the data grid or in the text area
20 | - convert the data in "Any Way" by converting between Markdown, CSV, JSON, Gherkin and HTML
21 | - auto generate the data in "Any Way" using Regex and Faker formats
22 |
23 | And the plan is to expand these ways in the future.
24 |
25 | This hopefully avoids the trap of the application:
26 |
27 | - just being seen as table data editor
28 | - just being seen as a test data generator
29 | - just being seen as a data format convertor
30 |
31 | Because while it is all of those things, it is not 'just' one of those things.
32 |
33 | The [code is still open source](https://github.com/eviltester/grid-table-editor/), I've just given it a domain to make it easier to find and remember.
34 |
--------------------------------------------------------------------------------
/docs-src/src/pages/privacy.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: privacy policy for AnyWayData.com
3 | description: privacy policy
4 | hide_table_of_contents: false
5 | ---
6 |
7 | # Privacy Policy
8 |
9 | We take your privacy seriously.
10 |
11 | AnyWayData does not track any data that you generate nor any content that you create or convert on the site. All data is generated and processed in your browser, none of it is passed to the server.
12 |
13 | ## Traffic Analysis
14 |
15 | We use [plausible.io](https://plausible.io/) to count traffic coming to the site. This gives us an idea of the number of visitors and which documentation pages are used. No personal data is collected using this service and the data is not shared with anyone else.
16 |
17 | ## Use of Google Products
18 |
19 | The documentation and blog have Google Ads enabled on them. This is too help pay for the ongoing maintenance and hosting for AnyWayData.
20 |
21 | In the event that companies choose to sponsor AnyWayData.com, this dependency on ad revenue could be removed. Until then...
22 |
23 | This text is from [Google's adsense privacy policy page](https://support.google.com/adsense/answer/1348695?hl=en-GB):
24 |
25 | - Google, uses cookies to serve ads based on your previous visits to this website or other websites.
26 | - Google's use of advertising cookies enables it and its partners to serve ads to you based on your visit to this site and/or other sites on the Internet.
27 | - You may opt out of personalised advertising by visiting [Ads Settings](https://adssettings.google.co.uk/authenticated). (Alternatively, you can opt out of a third-party vendor's use of cookies for personalised advertising by visiting [aboutads.info](https://www.aboutads.info).)
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-11-html-import.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: html-import-dsv-options
3 | title: HTML Table Import and CSV Delimiter Options
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-11T12:00
7 | ---
8 |
9 | Can now configure CSV and Generic Delimited Outputs, and added support for HTML Table input.
10 |
11 |
12 |
13 | ## Refactoring
14 |
15 | I've performed a lot of refactoring on the code to make adding more importers and exporters simpler.
16 |
17 | ## CSV Options
18 |
19 | I'm introducing options to the export panes to aid configuration of the output.
20 |
21 | The first export format to get this is the delimited output.
22 |
23 | Specifically CSV, so it is now possible to add or remove quotes, export without a header and change the quote or escape character when exporting to CSV.
24 |
25 | ## DSV - Delimiter Separated Values
26 |
27 | Internally CSV was always a special case of a Generic Delimiter Separated Value.
28 |
29 | I've exposed this to the GUI now so it is possible to export as:
30 |
31 | - tab delimited
32 | - various hard coded formats like Hash, Colon, etc.
33 | - custom delimiter to add your own delimiter
34 |
35 | The custom delimiter supports multi character delimiters so if you want to output a CSV file with a space after each comma then you can set that up on the `Delmited` output tab.
36 |
37 | ## HTML Table Import
38 |
39 | The first version of the HTML convertor only supported generating HTML.
40 |
41 | Now I've added an importer to allow importing `
` formatted HTML tables for editing in the data table editor.
42 |
43 | ## More to come
44 |
45 | I've added a lot of TODOs into the code to expand out the options and make notes on new import/export formats that the tool will support.
--------------------------------------------------------------------------------
/js/data_generation/rulesParser.js:
--------------------------------------------------------------------------------
1 | import {TestDataRules} from "./testDataRules.js";
2 |
3 | export class RulesParser{
4 |
5 | constructor(aFaker, RandExp){
6 | this.faker = aFaker;
7 | this.RandExp = RandExp;
8 | this.testDataRules = new TestDataRules();
9 | this.errors = [];
10 | }
11 |
12 | parseText(textContent){
13 |
14 | const defnLines = textContent.split("\n");
15 |
16 | if(defnLines.length%2 !==0){
17 | this.errors.push("ERROR: Specification should be ColumnName followed by RuleDefinition with an even number of lines");
18 | }
19 |
20 | if(defnLines.length === 0 || (defnLines.length === 1 && defnLines[0].length === 0)){
21 | this.errors.push("ERROR: No Rules Defined");
22 | return;
23 | }
24 |
25 | // add rules to dataDefn
26 | for(var index=0; index{
8 |
9 | test('will return empty array when blank', async () => {
10 |
11 | expect(getNumberArrayFrom(undefined)).toBeArrayOfSize(0);
12 | expect(getNumberArrayFrom(null)).toBeArrayOfSize(0);
13 | expect(getNumberArrayFrom("")).toBeArrayOfSize(0);
14 | expect(getNumberArrayFrom(" ")).toBeArrayOfSize(0);
15 | expect(getNumberArrayFrom(" ")).toBeArrayOfSize(0);
16 | });
17 |
18 | test('will ignore non number symbols', async () => {
19 | expect(getNumberArrayFrom("-- + ( ) pp s . > <")).toBeArrayOfSize(0);
20 | });
21 |
22 | test('will find buried numbers', async () => {
23 |
24 | let array = getNumberArrayFrom("-- + ( ) 3 pp s . > <");
25 | expect(array).toBeArrayOfSize(1);
26 | expect(array[0]).toBe(3);
27 | });
28 |
29 | test('can handle csv', async () => {
30 |
31 | let array = getNumberArrayFrom("4, 3, 2 ");
32 | expect(array).toBeArrayOfSize(3);
33 | expect(array[0]).toBe(4);
34 | expect(array[1]).toBe(3);
35 | expect(array[2]).toBe(2);
36 | });
37 |
38 |
39 | test('can handle mixed delims', async () => {
40 |
41 | let array = getNumberArrayFrom("4, 3 - 2 +99");
42 | expect(array).toBeArrayOfSize(4);
43 | expect(array[0]).toBe(4);
44 | expect(array[1]).toBe(3);
45 | expect(array[2]).toBe(2);
46 | expect(array[3]).toBe(99);
47 | });
48 |
49 | });
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-19-markdown-pretty-print.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: markdown-tables-pretty-print
3 | title: Pretty print option for Markdown Tables
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-20T12:00
7 | ---
8 |
9 | Can now pretty print Markdown table output to pad out the column width to the contents.
10 |
11 |
12 |
13 | ## Markdown Options
14 |
15 | I've added an option to the Markdown output now so it is possible to pretty print Markdown to pad out the cell widths in the same manner that AsciiTable markdown output and the Gherkin output does, but because it is combined with the other Markdown options it offers more output control.
16 |
17 | Because the Markdown heading separator can have alignment identifiers (":---") this required a slightly different algorithm.
18 |
19 |
20 | ## Markdown Tables Without Pretty Print
21 |
22 | The pretty print is purely cosmetic and if you are using the tables in a document to convert to HTML or PDF then you don't really need to print them.
23 |
24 | ```
25 | |firstname|lastname|
26 | |-----|-----|
27 | |Elvis|Donnelly|
28 | |Vella|Marks|
29 | |Jacques|Gislason|
30 | |Roxane|Okuneva|
31 | ```
32 |
33 | ## Markdown Tables With Pretty Print
34 |
35 | But, if your Markdown serves a dual purpose where the `.md` file should also be readable then pretty print can help.
36 |
37 | ```
38 | |firstname|lastname|
39 | |---------|--------|
40 | |Elvis |Donnelly|
41 | |Vella |Marks |
42 | |Jacques |Gislason|
43 | |Roxane |Okuneva |
44 | ```
45 |
46 | ## AnyWayData.com will import pretty printed tables
47 |
48 | Because AnyWayData.com will import pretty printed tables it becomes a useful tool for maintaining the tables.
49 |
50 | - Paste them in to the text area,
51 | - click "Set Grid From Text"
52 | - edit the data
53 | - Regenerate with Pretty Print
--------------------------------------------------------------------------------
/js/data_generation/faker/fakerTestDataRuleValidator.js:
--------------------------------------------------------------------------------
1 | import { FakerCommand } from "./fakerCommand.js";
2 |
3 | // requires faker.js which should be passed in via constructor
4 | // faker can be imported in different ways
5 |
6 | // https://fakerjs.dev/guide/#environments
7 | // https://cdn.skypack.dev/@faker-js/faker
8 |
9 | // use a moduleNameMapper in jest to allow importing from https
10 | // see package.json for the jest config
11 | //import { faker } from '@faker-js/faker';
12 | //import { faker } from "https://cdn.skypack.dev/@faker-js/faker@v9.7.0";
13 |
14 | class FakerTestDataRuleValidator{
15 |
16 | constructor(aFaker) {
17 | this.validationError="";
18 | this.faker = aFaker;
19 | }
20 |
21 | validate(aTestDataRule){
22 | this.validationError="";
23 |
24 | // is it a faker function?
25 | try{
26 | const fakerCommand = new FakerCommand(aTestDataRule.ruleSpec);
27 | fakerCommand.parse();
28 | fakerCommand.compile(this.faker);
29 | const whatDidWeGet = fakerCommand.validate(this.faker);
30 |
31 | if(whatDidWeGet !== undefined && whatDidWeGet !==null && whatDidWeGet.isError===false){
32 | aTestDataRule.type="faker";
33 | aTestDataRule.fakerCommand = fakerCommand.fakerFunctionName;
34 | return true;
35 | }else{
36 | this.validationError=whatDidWeGet.errorMessage;
37 | return false;
38 | }
39 | }catch(err){
40 | this.validationError=err;
41 | return false;
42 | }
43 | }
44 |
45 | isValid(){
46 | return this.validationError.length == 0;
47 | }
48 |
49 | getValidationError(){
50 | return this.validationError;
51 | }
52 | }
53 |
54 |
55 | export {FakerTestDataRuleValidator};
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-28-gherkin-table-data.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: gherkin-tables
3 | title: Added Gherkin Documentation
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-27T18:00
7 | ---
8 |
9 | We now have documentation for [Gherkin](/docs/data-formats/gherkin/gherkin) and [Options](/docs/data-formats/gherkin/options)
10 |
11 |
12 |
13 | ## Gherkin
14 |
15 | Gherkin is a simple human readable format for executable specifications.
16 |
17 | It is written in a `Given`, `When`, `Then` format and data tables are used to repeat the specification with different values.
18 |
19 | This makes it easy to specify lots of combinations of data for execution in a very readable manner.
20 |
21 | One of the most popular tools to implement Gherkin is [Cucumber](https://cucumber.io/docs/gherkin/reference/)
22 |
23 | For Example:
24 |
25 | ```
26 | Scenario Outline: Calculate World Population Share
27 | Given we know the stats for a Country named
28 | When we account for a specific count
29 | Then the calculated world populate percentage is
30 |
31 | Examples:
32 | | Country | Population | Share |
33 | | China | 1,439,323,776 | 18.47 % |
34 | | India | 1,380,004,385 | 17.70 % |
35 | | United States | 331,002,651 | 4.25 % |
36 | ```
37 |
38 | This is a human readable specification and you an imagine the ``, `` and `` values being picked up by repeating the specification for each value in the table.
39 |
40 | A programmer writes the code to execute for each of the `Given`, `When` and `Then` statements to allow the specification to be executed against an application to check if the functionality matches the specification and behaves as expected.
41 |
42 | AnyWayData offers support for both import and export of [Gherkin](/docs/data-formats/gherkin/gherkin) tables.
43 |
44 |
--------------------------------------------------------------------------------
/tests/data_generation/unit/faker/fakerTestDataRuleValidator.test.js:
--------------------------------------------------------------------------------
1 | import { TestDataRule } from '../../../../js/data_generation/testDataRule.js';
2 | import { FakerTestDataRuleValidator} from '../../../../js/data_generation/faker/fakerTestDataRuleValidator.js';
3 | import { faker } from '@faker-js/faker';
4 |
5 |
6 | describe("Can validate Faker TestDataRules using FakerTestDataRuleValidator",()=>{
7 |
8 |
9 | test('can determine a invalid Faker TestDataRule', () => {
10 | const rule = new TestDataRule("Test", "internet.ea");
11 | rule.type="faker";
12 |
13 | const validator = new FakerTestDataRuleValidator(faker);
14 | validator.validate(rule);
15 | expect(validator.isValid()).toBe(false);
16 |
17 | });
18 |
19 | test('can determine a invalid Faker TestDataRule does not match faker call', () => {
20 | const rule = new TestDataRule("Test", "internet");
21 | rule.type="faker";
22 |
23 | const validator = new FakerTestDataRuleValidator(faker);
24 | validator.validate(rule);
25 | expect(validator.isValid()).toBe(false);
26 |
27 | });
28 |
29 | test('can determine a syntax error for Faker TestDataRule', () => {
30 | const rule = new TestDataRule("Test", "internet.email('bob'");
31 | rule.type="faker";
32 |
33 | const validator = new FakerTestDataRuleValidator(faker);
34 | validator.validate(rule);
35 | expect(validator.isValid()).toBe(false);
36 |
37 | });
38 |
39 | test('will not process empty Faker TestDataRule', () => {
40 | const rule = new TestDataRule("Test", "");
41 | rule.type="faker";
42 |
43 | const validator = new FakerTestDataRuleValidator(faker);
44 | validator.validate(rule);
45 | expect(validator.isValid()).toBe(false);
46 |
47 | });
48 | });
49 |
50 |
--------------------------------------------------------------------------------
/docs-src/blog/2022-07-07-json-javascript.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: json-javascript-import-export
3 | title: Added Json and Javascript Documentation
4 | authors: alan
5 | tags: [release]
6 | date: 2022-07-07T18:00
7 | ---
8 |
9 | We now have documentation for [JSON](/docs/data-formats/json/json) and [Javascript](/docs/data-formats/javascript/javascript).
10 |
11 | I also changed the Javascript generation to not use quoted keys so it is now distinct from the JSON output.
12 |
13 |
14 |
15 | ## JSON
16 |
17 | JSON is a subset of Javascript and only has the Javascript syntax to represent data.
18 |
19 | This is often used as a programming language independent data exchange format.
20 |
21 | It is easy to read by humans and easy to process automatically.
22 |
23 | This makes it very suitable for data interchange over APIs.
24 |
25 | In JSON all the keys in the key value pairs are quoted e.g.
26 |
27 | ```
28 | [
29 | {
30 | "name": "Anais",
31 | "id": "32468"
32 | },
33 | {
34 | "name": "Astrid",
35 | "id": "46731"
36 | }
37 | ]
38 | ```
39 |
40 | By default all numbers are quoted but it is possible to use the `Number Convert` option to change this e.g.
41 |
42 | ```
43 | [
44 | {
45 | "name": "Anais",
46 | "id": 32468
47 | },
48 | {
49 | "name": "Astrid",
50 | "id": 46731
51 | }
52 | ]
53 | ```
54 |
55 | In Javascript the keys are not quoted and my only contain valid javascript characters e.g. no spaces:
56 |
57 | ```
58 | [
59 | {
60 | customer_name: "Isabelle",
61 | id: "11996"
62 | },
63 | {
64 | customer_name: "Minerva",
65 | id: "86982"
66 | }
67 | ]
68 | ```
69 |
70 | ## Import and Export
71 |
72 | AnyWayData supports both the import and export of Json or Javascript object arrays.
73 |
74 | These can then be converted to any supported format of AnyWayData e.g. Convert JSON to CSV, Markdown Tables, Tab Delimited, Gherkin Tables, HTML or Ascii Tables.
75 |
76 |
--------------------------------------------------------------------------------
/js/data_generation/testDataGenerator.js:
--------------------------------------------------------------------------------
1 | import {RulesParser} from "./rulesParser.js";
2 | import {RulesBasedDataGenerator} from "./rulesBasedDataGenerator.js"
3 | import {TestDataRulesCompiler} from "./testDataRulesCompiler.js";
4 |
5 | /*
6 | This is the main entry point for data generation.
7 |
8 | Used by the CLI and the Web.
9 |
10 | Given a specification of data via importSpec, the data would
11 | then be compiled and could then generate.
12 | */
13 | export class TestDataGenerator{
14 |
15 | constructor(aFaker, aRandExp){
16 | this.faker = aFaker;
17 | this.RandExp = aRandExp;
18 | this.rulesParser = new RulesParser(aFaker, aRandExp);
19 | this.generator = new RulesBasedDataGenerator(aFaker, aRandExp);
20 | this.compiler = new TestDataRulesCompiler(aFaker, aRandExp);
21 | }
22 |
23 | importSpec(textContent){
24 | this.rulesParser.parseText(textContent);
25 | }
26 |
27 | compile(){
28 | // validate and assign rules
29 | this.compiler.compile(this.rulesParser.testDataRules.rules);
30 | this.compiler.validate();
31 | }
32 |
33 | compilationReport(){
34 | return this.compiler.compilationReport();
35 | }
36 |
37 | testDataRules(){
38 | return this.rulesParser.testDataRules.rules;
39 | }
40 |
41 | isValid(){
42 | return this.errors().length === 0;
43 | }
44 |
45 | errors(){
46 | return this.rulesParser.errors.concat(this.compiler.errors);
47 | }
48 |
49 | generate(thisMany){
50 | return this.generator.generateFromRules(thisMany, this.rulesParser.testDataRules.rules);
51 | }
52 |
53 | generateHeadersArray(){
54 | return this.rulesParser.testDataRules.rules.map((rule) => rule.name);
55 | }
56 |
57 | generateRow(){
58 | return this.generator.generateRandomRow(this.rulesParser.testDataRules.rules);
59 | }
60 | }
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/gherkin/010-gherkin.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "Gherkin"
4 | description: "The Gherkin data format is a tabular format for feeding data values into an executable specification or BDD style scenario."
5 | ---
6 |
7 | The Gherkin data format is a tabular format for feeding data values into an executable specification or BDD style scenario.
8 |
9 | ## What is Gherkin
10 |
11 | Gherkin is a tabular ascii format, much like the Markdown table format, used in BDD style tools like [Cucumber](https://cucumber.io/).
12 |
13 | One of the main difference with Markdown is that there is no delimiter row to separate the heading from the data rows.
14 |
15 | The table is embedded within a scenario description either in the main text for a Given, When, or Then statement. Or as a set of Examples.
16 |
17 | ```
18 | Scenario Outline: Calculate World Population Share
19 | Given we know the stats for a Country named
20 | When we account for a specific count
21 | Then the calculated world populate percentage is
22 |
23 | Examples:
24 | | Country | Population | Share |
25 | | China | 1,439,323,776 | 18.47 % |
26 | | India | 1,380,004,385 | 17.70 % |
27 | | United States | 331,002,651 | 4.25 % |
28 | ```
29 |
30 | A few Gherkin References:
31 |
32 | - [Cucumber Gherkin Reference](https://cucumber.io/docs/gherkin/reference/)
33 | - [Data Table Code Examples in Java](https://www.tutorialspoint.com/cucumber/cucumber_data_tables.htm)
34 | - [More Data Table Examples in Java](https://www.ontestautomation.com/using-data-tables-in-cucumber-jvm-for-better-readable-specifications/)
35 |
36 | ## Support For Gherkin
37 |
38 | AnyWayData supports importing and exporting Gherkin tables.
39 |
40 | These can then be converted to other formats. More likely you will convert HTML table data or CSV data to Gherkin for use in a BDD executable specification.
--------------------------------------------------------------------------------
/js/gui_components/options_panels/options-ascii-table.js:
--------------------------------------------------------------------------------
1 | import {AsciiTableConvertor, AsciiTableOptions} from "../../data_formats/asciitable-convertor.js"
2 | import {HtmlDataValues} from "./html-options-data-utils.js";
3 |
4 | class AsciiTableOptionsPanel{
5 |
6 | constructor(parentElement) {
7 | this.parent = parentElement;
8 | this.htmlData = new HtmlDataValues(this.parent);
9 | }
10 |
11 | addToGui(){
12 |
13 | let stylesAsOptions = "";
14 | let asciiTableConverter = new AsciiTableConvertor();
15 |
16 | for (const [readable, internal] of Object.entries(asciiTableConverter.options.styleNames)) {
17 | stylesAsOptions = stylesAsOptions +
18 | ``
19 | }
20 |
21 | this.parent.innerHTML =
22 | `
23 |
24 |
Options
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | `;
41 | }
42 |
43 | setApplyCallback(callbackFunc){
44 | let button = this.parent.querySelector(".delimited-options .apply button");
45 | button.onclick = function (){
46 | callbackFunc(this.getOptionsFromGui())
47 | }.bind(this);
48 | }
49 |
50 | getOptionsFromGui(){
51 | let newOptions = new AsciiTableOptions();
52 | newOptions.options.style = this.htmlData.getSelectedValueFrom("div.style select","ramac");
53 | return newOptions;
54 | }
55 |
56 | setFromOptions(asciiTableOptions){
57 | this.htmlData.setDropDownOptionToKeyValue("div.style select", asciiTableOptions.options.style, "default");
58 | }
59 | }
60 |
61 | export {AsciiTableOptionsPanel};
--------------------------------------------------------------------------------
/docs-src/docs/020-editing-data/010-editable-grid.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "Editable Data Grid"
4 | description: "Learn to use AnyWayData.com Editable Data Grid to edit tabular data from multiple formats, add and delete rows and columns to customize the data exports."
5 | ---
6 |
7 | # Edit Data in The Grid
8 |
9 | The main data grid is where you can edit the data.
10 |
11 | Initially it is populated by scraping the summary instructions and adding them to the grid. This is just to show some data and help get started.
12 |
13 | - Edit data in any cell by double clicking on the cell.
14 |
15 | Data in the grid is not automatically converted to the text area data. To see the data in the grid formatted as per the tab title either"
16 |
17 | - click the `v Set Text From Grid v` button
18 | - select a new format tab
19 |
20 | ## Adding and Removing Rows
21 |
22 | The buttons above the grid allow you to edit the rows.
23 |
24 | - Add Row - adds a row at the bottom of the grid
25 |
26 | If you have selected one or more rows then you can:
27 |
28 | - Add Rows Above
29 | - Add Rows Below
30 | - Delete Selected Rows
31 |
32 | ## Adding and Removing Columns
33 |
34 | Each column has a set of buttons in the header.
35 |
36 | - `[<+]` will add a new column to the left of this column
37 | - `[~]` allows you to rename the column
38 | - `[x]` allows you to delete the column
39 | - `[+=]` allows you to duplicate the column
40 | - `[+>]` will add a new column to the right of this column
41 |
42 | ## Filtering Data
43 |
44 | The `Filter:` text area above the grid can be used to globally search and filter the data in the grid.
45 |
46 | Any text entered here will be matched in the grid an only rows that have the entered text will be shown.
47 |
48 | Pressing the `[Clear Filter]` button will clear the global filter.
49 |
50 | Each column can be filtered by using the `filter` symbol to show a filter menu for the column.
51 |
52 | ## Ordering Columns and Rows
53 |
54 | You can drag and drop both rows and columns to re-order them.
55 |
56 | ## Clearing The Grid
57 |
58 | All data in the grid can be deleted by pressing the `[Reset Table]` button.
59 |
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/delimited/options.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: "Delimited Options"
4 | description: "Options available for converting to Delimited files in AnyWayData.com. This could be tab delimited, pipe delimited or any other delimited file configuration required."
5 | ---
6 |
7 | The configuration options for Delimited data sets are listed below. These are similar to [CSV](/docs/data-formats/csv/csv) but are more flexible because you can control the delimiter value.
8 |
9 | ## Delimiter
10 |
11 | The `Delimiter` select option offers a choice of the most common delimiters. A Select drop down is used because some values like Tab can be hard to input in a form.
12 |
13 | Choose from:
14 |
15 | - Tab [\\t]
16 | - Comma [,]
17 | - Hash [#]
18 | - Colon [:]
19 | - Pipe [|]
20 | - Space [ ]
21 | - Semicolon [;]
22 | - Slash [/]
23 | - Slash [\\]
24 | - Custom Value
25 |
26 | The `Custom Value` option should be chosen when you want to use the value entered as the `Custom` input.
27 |
28 | ## Custom
29 |
30 | The `Custom` text area allows you to enter any delimiter that you want. This can be a multi-character input, you are not restricted to a single character.
31 |
32 | ## Use Quotes
33 |
34 | The `Use Quotes` options allows you to configure when quotes are added to the values:
35 |
36 | - when checked, then every value in the delimited file will be wrapped with quotes
37 | - when unchecked, the only values containing quotes or the delimiter will be wrapped with quotes
38 |
39 | ## Use Header
40 |
41 | `Use Header` configures if the row header will be added to the first line of the delimited file or not.
42 |
43 | ## Quote Char
44 |
45 | The `Quote Char` option configures the quote character to use.
46 |
47 | By default this will be `"` but some tools may prefer `'`, and if so you can reconfigure the output.
48 |
49 | ## Escape Char
50 |
51 | The `Escape Char` option configures the character that will be used in front of the `Quote Char` when the `Quote Char` is present in the value.
52 |
53 | Most of the time this will be the same as the `Quote Char` itself, but you may need to configure it to `\` or some other value for other tools.
--------------------------------------------------------------------------------
/docs-src/docs/040-test-data/010-test-data-generation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "Random Data"
4 | description: "Generate random data for the grid"
5 | ---
6 |
7 | # Test Data Generation
8 |
9 | By 'opening' the `Test Data` section in the GUI it is possible to Generate a Data Grid filled with Random Data.
10 |
11 | ## Test Data Grid
12 |
13 | The Test Data Grid contains the 'schema' or 'template' to use to generate data for the grid.
14 |
15 | Each row represents a Column in the final data grid.
16 |
17 | Add a new column by pressing the `+ Add Column` button.
18 |
19 | You can rename the column by double clicking on the `Column Name` field.
20 |
21 | The Type is the `type` of data that will be generated in the column. This can be a `RegEx` (Regular Expression), or one of the predefined random data types e.g. `random.word` or `random.boolean` etc.
22 |
23 | ## Types
24 |
25 | The Types of data available can be chosen from the drop down value.
26 |
27 | The drop down is an `option` select so you can type in a filter e.g `email` and you will see any matching options for email.
28 |
29 | ## Number of Rows to Generate
30 |
31 | Configure how many rows of data to generate by typing the numeric value in the `How Many?` text field.
32 |
33 | e.g. to generate 1000 rows of data enter `1000` in the `How Many?` text input field.
34 |
35 | ## Generating Data
36 |
37 | Press the `[Generate]` button to generate the data.
38 |
39 | The schema in the Column Definition Data Grid will be used to generate the data.
40 |
41 | All data generation happens in the browser so the amount of data you can generate is limited only by the performance and memory of your computer.
42 |
43 | ## Text Area
44 |
45 | When you add rows using the data grid, you will see the information is also copied into the text area in the right. This allows you to copy and paste schema definitions for re-use.
46 |
47 | The Schema format in the Text Area uses the format
48 |
49 | ```
50 | column name
51 | type value
52 | ```
53 |
54 | e.g.
55 |
56 | ```
57 | column 1
58 | 1234
59 | ```
60 |
61 | When no type is present it is assumed to be a Regex e.g. `1234` is a Regex that represents the string "1234"
--------------------------------------------------------------------------------
/docs-src/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import Link from '@docusaurus/Link';
4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
5 | import Layout from '@theme/Layout';
6 | import HomepageFeatures from '@site/src/components/HomepageFeatures';
7 | import ReactPlayer from 'react-player';
8 |
9 | import styles from './index.module.css';
10 |
11 | function HomepageHeader() {
12 | const {siteConfig} = useDocusaurusContext();
13 | return (
14 |
15 |
40 |
41 |
42 | );
43 | }
44 |
45 | export default function Home() {
46 | const {siteConfig} = useDocusaurusContext();
47 | return (
48 |
51 |
52 |
53 |
54 |
59 |
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/docs-src/docs/040-test-data/020-regex-test-data.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: "Regex Based Data"
4 | description: "Generate data from Regex"
5 | ---
6 |
7 | # Regex Data Generation
8 |
9 | Regex Strings are normally used for 'matching' data e.g. searching for data that matches a particular format.
10 |
11 | Regex can be used in reverse to generate data. Care has to be taken that limits are added to lengths of character sequences, otherwise 'in theory' strings could be of infinite length.
12 |
13 | e.g. `[\d]*` could 'in theory' generate an infinite string of digits.
14 |
15 | Under the covers we are using [RandExp](https://www.npmjs.com/package/randexp) as the library to generate strings from regular expressions and uses a limit of 100 as a default upper bound if no upper bound is provided in the regex value.
16 |
17 | Limits can be supplied for length using the `{min,max}` syntax e.g. `[\d]{2,11}` generate between 2 and 11 length strings of digits.
18 |
19 |
20 | ## Useful Regex Tools
21 |
22 | Regex Testers, these can be used to see if a particular Regex matches a list of Strings. In general, if the Regex can be used to match the string then it can also be used to generate the string.
23 |
24 | - [regexr.com](https://regexr.com/)
25 | - [regex101.com](https://regex101.com/)
26 | - [RegexTester.com](https://www.regextester.com/)
27 |
28 | ## Useful Regex Cheatsheets
29 |
30 | - [rexegg.com/regex-quickstart.html](https://www.rexegg.com/regex-quickstart.html)
31 |
32 | ## Useful Regex Sites
33 |
34 | - [regexone.com](https://regexone.com) Interactive Regex tutorial
35 | - [regular-expressions.info](https://www.regular-expressions.info)
36 | - [rexegg.com](https://www.rexegg.com)
37 |
38 |
39 | ## Examples
40 |
41 | ### Words of Random Length
42 |
43 | e.g. `[A-Z ]{3,12}`
44 |
45 | By using a character group `[A-Z ]` which contains the letters A to Z and the space character we can generate a string between 3 and 12 characters long `{3,12}`.
46 |
47 | For example, this could be used to generate a table with the multiple columns of random 'word' lengths:
48 |
49 |
50 | ```
51 | col1
52 | [A-Z ]{3,12}
53 | col2
54 | [A-Z ]{5,12}
55 | col3
56 | [A-Z ]{1,12}
57 | ```
58 |
59 |
--------------------------------------------------------------------------------
/docs-src/blog/2022-08-01-more-faker-compatibility.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: expanded-faker-compatibility
3 | title: Improved Data Generation
4 | authors: alan
5 | tags: [release]
6 | date: 2022-08-01T18:55
7 | ---
8 |
9 | We have enhanced the faker compatibility for test data generation so it is now possible to parameterise the faker function calls.
10 |
11 |
12 |
13 | ## Faker
14 |
15 | [Faker](https://fakerjs.dev/guide/) is the test data generation library used by AnyWayData.
16 |
17 | It has an extensive API of function calls. e.g. `location.buildingNumber` to generate a random building number.
18 |
19 | In the test data type definition drop down you can find a list of faker function calls in the drop down used on the Test Data Generation data grid.
20 |
21 | The items in the drop down are a subset of the function calls, without any parameters.
22 |
23 | When you read through the API documentation for faker you will see a lot of options available for the API calls. These are now also available from AnyWayData by typing the data spec into the text area editor.
24 |
25 | AnyWayData also implements most of the helper functionality to create templates and fake data.
26 |
27 | e.g.
28 |
29 | ```
30 | address
31 | helpers.fake('{{location.buildingNumber}} {{location.streetAddress}}')
32 | Sentence
33 | helpers.mustache('I found {{count}} instances of "{{word}}".', {count: () => `${this.number.int()}`,word: "this word",})
34 | ```
35 |
36 | Could generate:
37 |
38 | ```
39 | "address","Sentence"
40 | "333 798 N Main Avenue","I found 4485653369866095 instances of ""this word""."
41 | "2065 866 Theodora Corners","I found 6979689506928155 instances of ""this word""."
42 | "4405 147 Alvena Mountain","I found 646049465038696 instances of ""this word""."
43 | ```
44 |
45 | [Learn more about the Faker functionality in the docs](https://anywaydata.com/docs/test-data/faker-test-data)
46 |
47 | ## Summary
48 |
49 | You can now use most of the API functions listed in the Faker API documenation and create very complicated data sets using the parameters for the API.
50 |
51 | Read through the Faker docs and experiment.
52 |
53 | [fakerjs.dev/api/helpers.html#mustache](https://fakerjs.dev/api/helpers.html#mustache)
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grid-table-editor",
3 | "version": "1.0.0",
4 | "description": "A simple Data Table Editor that generates Markdown, CSV or JSON. It can also be used to interchange data between the formats, supporting editing in the grid.",
5 | "main": "index.html",
6 | "scripts": {
7 | "test": "jest",
8 | "testverbose": "jest --verbose",
9 | "testcoverage": "jest --coverage",
10 | "anywaydata:mac": "npm run anywaydata --prefix docs-src && cp ./index.html ./build/app.html && cp -R ./images ./build/images && cp -R ./js ./build/js && cp -R ./libs ./build/libs && cp ./styles.css ./build/styles.css && cp -R ./build/ ../../anywaydata-com",
11 | "anywaydata:win": "npm run anywaydata --prefix docs-src && echo copying index && copy .\\index.html .\\build\\app.html && echo copying images && xcopy .\\images .\\build\\images /e /k /i && xcopy .\\js .\\build\\js /e /k /i && xcopy .\\libs .\\build\\libs /e /k /i && copy .\\styles.css .\\build\\styles.css && echo cleaning anywaydata for deployment && ..\\anywaydata-com\\make-ready-for-prod.bat",
12 | "previewdocs": "npm run start --prefix docs-src"
13 | },
14 | "jest": {
15 | "verbose": true,
16 | "moduleNameMapper": {
17 | "https://cdn.skypack.dev/@faker-js/faker@v9.7.0": "@faker-js/faker",
18 | "https://unpkg.com/papaparse@5.3.2/papaparse.min.js": "papaparse",
19 | "/libs/randexp.min.js": "randexp"
20 | }
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/eviltester/grid-table-editor.git"
25 | },
26 | "keywords": [
27 | "table editor",
28 | "test data",
29 | "test data generator",
30 | "markdown table",
31 | "html table",
32 | "data grid editor"
33 | ],
34 | "author": "Alan Richardson",
35 | "license": "MIT",
36 | "bugs": {
37 | "url": "https://github.com/eviltester/grid-table-editor/issues"
38 | },
39 | "homepage": "https://github.com/eviltester/grid-table-editor#readme",
40 | "dependencies": {
41 | "jest": "^28.1.0",
42 | "jsdom": "^20.0.0",
43 | "papaparse": "^5.3.2",
44 | "randexp": "^0.5.3"
45 | },
46 | "devDependencies": {
47 | "@babel/preset-env": "^7.18.0",
48 | "@faker-js/faker": "^9.7.0",
49 | "babel-jest": "^28.1.0",
50 | "jest-extended": "^2.0.0"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/js/gui_components/data-grid-editor/ag-grid/select-filter-editor.js:
--------------------------------------------------------------------------------
1 | // create a simple editor that shows the list of values but with a search component
2 | // https://www.ag-grid.com/javascript-data-grid/component-cell-editor/
3 | /*
4 | */
9 |
10 |
11 | class SelectFilterEditor {
12 | init(params) {
13 | this.value = params.value;
14 |
15 | this.gui = document.createElement('div');
16 | this.input = document.createElement('input');
17 | this.gui.appendChild(this.input);
18 | this.input.setAttribute("list", 'selectionValuesDataList');
19 | let datalist = document.createElement('datalist');
20 | datalist.id="selectionValuesDataList";
21 | this.gui.appendChild(datalist);
22 | //console.log(params.values);
23 | params.values.forEach((value)=>{
24 | var option = document.createElement("option");
25 | option.setAttribute("value",value);
26 | datalist.appendChild(option);
27 | })
28 | this.validValues = params.values;
29 |
30 | this.input.addEventListener('input', (event) => {
31 | this.value = event.target.value;
32 | });
33 | }
34 |
35 | /* Component Editor Lifecycle methods */
36 | // gets called once when grid ready to insert the element
37 | getGui() {
38 | return this.gui;
39 | }
40 |
41 | // the final value to send to the grid, on completion of editing
42 | getValue() {
43 | return this.value;
44 | }
45 |
46 | // Gets called once before editing starts, to give editor a chance to
47 | // cancel the editing before it even starts.
48 | isCancelBeforeStart() {
49 | return false;
50 | }
51 |
52 | // Gets called once when editing is finished (eg if Enter is pressed).
53 | // If you return true, then the result of the edit will be ignored.
54 | isCancelAfterEnd() {
55 | // value must be from the list
56 | return !this.validValues.includes(this.value);
57 | }
58 |
59 | // after this component has been created and inserted into the grid
60 | afterGuiAttached() {
61 | this.input.focus();
62 | }
63 | }
64 |
65 | export {SelectFilterEditor}
--------------------------------------------------------------------------------
/js/script.js:
--------------------------------------------------------------------------------
1 | import { Importer } from "./grid/importer.js";
2 | import { Exporter } from "./grid/exporter.js";
3 | import { enableTestDataGenerationInterface } from "./gui_components/testdatadefn.js";
4 | import { ExtendedDataGrid } from "./gui_components/data-grid-editor/ag-grid/main-display-grid.js";
5 | import { TabbedTextControl } from "./gui_components/tabbed-text-control.js"
6 | import { ImportExportControls } from "./gui_components/import-export-controls.js";
7 | import { GenericDataTable } from "./data_formats/generic-data-table.js";
8 |
9 | var importer, exporter;
10 | var mainDataGrid;
11 | var importExportController;
12 |
13 |
14 | // setup the grid after the page has finished loading
15 | document.addEventListener('DOMContentLoaded', function() {
16 |
17 | let mainGridArea = document.getElementById("main-grid-view");
18 | mainDataGrid = new ExtendedDataGrid();
19 | mainDataGrid.createChildGrid(mainGridArea);
20 |
21 | importExportController = new ImportExportControls();
22 | importExportController.addHTMLtoGui(document.getElementById("import-export-controls"));
23 |
24 | new TabbedTextControl(document.getElementById("tabbedTextArea"), importExportController).addToGui();
25 |
26 |
27 | exporter = new Exporter(mainDataGrid.getGridExtras());
28 | importer = new Importer(mainDataGrid.getGridExtras());
29 | importExportController.setExporter(exporter);
30 | importExportController.setImporter(importer);
31 |
32 |
33 | // give a clue what to do by importing the instructions into the grid
34 | setTextFromInstructions();
35 |
36 | importExportController.renderTextFromGrid();
37 | importExportController.setFileFormatType();
38 | importExportController.setOptionsViewForFormatType();
39 |
40 | enableTestDataGenerationInterface("testDataGeneratorContainer", importer, importExportController.getExportControls());
41 |
42 | });
43 |
44 |
45 | function setTextFromInstructions(){
46 | mainDataGrid.getGridExtras().clearGrid();
47 | const instructions = document.querySelectorAll("div.instructions details ul li");
48 | let textData = new GenericDataTable();
49 | textData.addHeader("Instructions");
50 | instructions.forEach(instruction => {
51 | textData.appendDataRow([instruction.textContent]);
52 | })
53 | importer.setGridFromGenericDataTable(textData);
54 | // set the instructions to fit grid size
55 | mainDataGrid.sizeColumnsToFit()
56 | }
57 |
--------------------------------------------------------------------------------
/js/gui_components/drag-drop-control.js:
--------------------------------------------------------------------------------
1 | class DragDropControl {
2 |
3 | constructor(readFileFunction) {
4 | this.readFileFunction = readFileFunction;
5 | }
6 |
7 | // file drop handling
8 | // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
9 | // https://css-tricks.com/drag-and-drop-file-uploading/
10 |
11 | configureAsDropZone(element){
12 |
13 | if(typeof element === 'undefined') return;
14 | if( element === null) return;
15 |
16 | element.classList.add("dragdropzone");
17 |
18 | ['drag','dragstart',
19 | 'dragend', 'dragover',
20 | 'dragenter', 'dragleave'].
21 | forEach(e => element.addEventListener(e, function(e) {
22 | e.preventDefault();
23 | e.stopPropagation();
24 | }, false));
25 |
26 | ['dragover','dragenter'].
27 | forEach(e => element.addEventListener(e, function(e) {
28 | element.classList.add('is-dragover')
29 | }, false));
30 |
31 | ['dragleave','dragend','drop'].
32 | forEach(e => element.addEventListener(e, function(e) {
33 | element.classList.remove('is-dragover')
34 | }, false));
35 |
36 | this.onDropListener = this.fileDropHandler.bind(this);
37 | element.addEventListener('drop',this.onDropListener);
38 |
39 | }
40 |
41 | fileDropHandler(ev) {
42 | console.log('File(s) dropped');
43 |
44 | // Prevent default behavior (Prevent file from being opened)
45 | ev.preventDefault();
46 | ev.stopPropagation();
47 |
48 | if (ev.dataTransfer.items) {
49 | // Use DataTransferItemList interface to access the file(s)
50 | for (var i = 0; i < ev.dataTransfer.items.length; i++) {
51 | // If dropped items aren't files, ignore them
52 | if (ev.dataTransfer.items[i].kind === 'file') {
53 | var file = ev.dataTransfer.items[i].getAsFile();
54 | console.log('... file[' + i + '].name = ' + file.name);
55 | this.readFileFunction(file);
56 | break; // only process first file
57 | }
58 | }
59 | } else {
60 | this.readFileFunction(ev.dataTransfer.files[i]);
61 | }
62 | }
63 | }
64 |
65 | export {DragDropControl}
--------------------------------------------------------------------------------
/js/data_formats/delimiter-convertor.js:
--------------------------------------------------------------------------------
1 | import { DelimiterOptions } from "./delimiter-options.js";
2 | import { GenericDataTableUtils } from "./generic-data-table-utils.js";
3 | import { GenericDataTable } from "./generic-data-table.js";
4 | import { JsonConvertor } from "./json-convertor.js";
5 |
6 |
7 | export class DelimiterConvertor {
8 |
9 | constructor(params) {
10 |
11 | this.delimiterOptions = new DelimiterOptions();
12 |
13 | if(params!==undefined){
14 | this.setOptions(params);
15 | }
16 | }
17 |
18 | setOptions(newOptions){
19 |
20 | this.delimiterOptions.mergeOptions(newOptions);
21 | }
22 |
23 | setPapaParse(papaparse){
24 | this.papaparse=papaparse;
25 | }
26 |
27 | fromDataTable(dataTable){
28 | let objects = new JsonConvertor().formatAsObjects(dataTable);
29 | return this.papaparse.unparse(objects, this.delimiterOptions.options);
30 | }
31 |
32 | toDataTable(textToImport){
33 | let headerText="";
34 |
35 | if(this.delimiterOptions){
36 | if(this.delimiterOptions.options.header==false){
37 | // if we have any stored headers then add them to the input
38 | let headers = this.delimiterOptions.headers;
39 | if(headers!==undefined && headers.length>0){
40 | headerText = this.papaparse.unparse([this.delimiterOptions.headers], this.delimiterOptions.options)
41 | headerText = headerText+this.delimiterOptions.options.newline;
42 | }
43 |
44 | }
45 | }
46 |
47 | let rememberOptionsHeader=undefined;
48 |
49 | // we want PapaParse to return an array of nested arrays with first being headers
50 | if(this.delimiterOptions){
51 | rememberOptionsHeader = this.delimiterOptions.options.header;
52 | this.delimiterOptions.options.header=false;
53 | }
54 |
55 | var results=this.papaparse.parse(headerText + textToImport, this.delimiterOptions.options);
56 |
57 | if(rememberOptionsHeader!==undefined){
58 | this.delimiterOptions.options.header=rememberOptionsHeader;
59 | }
60 |
61 | let dataTable = new GenericDataTable();
62 | let utils = new GenericDataTableUtils();
63 | utils.setGenericDataTableFromDataArray(dataTable, results.data);
64 | return dataTable;
65 | }
66 | }
--------------------------------------------------------------------------------
/tests/data_formats/papa-parse.test.js:
--------------------------------------------------------------------------------
1 | import Papa from 'papaparse';
2 |
3 | describe("Check papa parse can meet multi char delimiter requirements",()=>{
4 |
5 | test('basic one char example to check it all works', () => {
6 |
7 | let parsed = Papa.parse("a,b,c",{header: false, delimiter:','});
8 | expect(parsed.data.length).toBe(1);
9 | expect(parsed.data[0][0]).toBe("a");
10 | })
11 |
12 | test(',space as simple delimiter', () => {
13 |
14 | let parsed = Papa.parse("a, b, c",{header: false, delimiter:', '});
15 | expect(parsed.data.length).toBe(1);
16 | expect(parsed.data[0][0]).toBe("a");
17 | })
18 |
19 | test(',space as simple delmiter with multi line input', () => {
20 | let inputData = `"test", "test2"
21 | "123a", "456a"
22 | "123b", "456b"
23 | "123c", "456c"`
24 | let parsed = Papa.parse(inputData,{quotes:true, quoteChar: '"', header: false, delimiter:', '});
25 | expect(parsed.data.length).toBe(4);
26 | expect(parsed.data[0][0]).toBe("test");
27 | expect(parsed.data[1][0]).toBe("123a");
28 | })
29 |
30 | test(',space as delimiter and experiment with options', () => {
31 | let inputData = `"test", "test2"
32 | "123a", "456a"
33 | "123b", "456b"
34 | "123c", "456c"`
35 | let parsed = Papa.parse(inputData,
36 | { quotes:true,
37 | quoteChar: '"',
38 | header: false,
39 | delimiter:', ',
40 | delimiter: ", ",
41 | dynamicTyping: false,
42 | escapeChar: "\"",
43 | newline: "\n",
44 | transform: false
45 | });
46 | expect(parsed.data.length).toBe(4);
47 | expect(parsed.data[0][0]).toBe("test");
48 | expect(parsed.data[1][0]).toBe("123a");
49 | })
50 |
51 | test('string as arbitrary delimiter', () => {
52 | let inputData = `"test" bob "test2"
53 | "123a" bob "456a"
54 | "123b" bob "456b"
55 | "123c" bob "456c"`
56 | let parsed = Papa.parse(inputData,{quotes:true, quoteChar: '"', header: false, delimiter:' bob '});
57 | expect(parsed.data.length).toBe(4);
58 | expect(parsed.data[0][0]).toBe("test");
59 | expect(parsed.data[1][0]).toBe("123a");
60 | })
61 |
62 | })
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-26-added-docs-for-csv-delimited.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: docs-csv-delimited
3 | title: Added Documentation for CSV and Delimited Files
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-26T13:00
7 | ---
8 |
9 | We now have documentation for [CSV](/docs/data-formats/csv/csv) and [Delimited](/docs/data-formats/delimited/delimited)
10 |
11 |
12 |
13 | ## Delimited Data
14 |
15 | CSV, or Comma Separated Values, is a special case of a Delimited file, but we have separate support for CSV simply because it is the most popular delimited data format.
16 |
17 | There are no formal standards for delimited data. The general conventions for delimited data are:
18 |
19 | - option first line defines the header values
20 | - every line after the header represents a line of data
21 | - a delimiter character is used to separate fields e.g. "," in the case of CSV
22 | - values are quoted when they contain a quote or a delimiter
23 | - some delimiter files quote all fields
24 |
25 | This is a very flexible input and output format and we support many configuration options:
26 |
27 | - [CSV Configuration Options](/docs/data-formats/csv/options)
28 | - [Delimited Configuration Options](/docs/data-formats/delimited/options)
29 |
30 |
31 | ## Tips
32 |
33 | Most tools that handle data will be able to import CSV so this is the most flexible for data interchange using files.
34 |
35 | I generally Use Quotes for all values so that they are wrapped with quotes as this provides the widest compatibility between tools.
36 |
37 | When copy and pasting tabular data between tools, Tab delimited is a more common format and is likely to work best.
38 |
39 | ## Conversion
40 |
41 | Because any of the data formats in AnyWayData.com can be converted to any other format you use AnyWayData.com to:
42 |
43 | - import CSV and Delimited files
44 | - export to CSV and Delimited files
45 | - edit imported data
46 | - re-order and delete columns from CSV or Tab delimited files
47 | - delete rows from CSV or Delimited files
48 |
49 | This makes AnyWayData.com a suitable tool for ad-hoc editing of data prior to importing into a Data Analysis tool or spreadsheet.
50 |
51 | Also the conversion to Ascii Table formats, including Markdown, make it easy to create human readable versions of delimited tool output.
52 |
53 | If you are testing and automating an application while using a BDD style tool, then you can easily import CSV or Tab delimited data, filter and edit it prior to exporting to Gherkin for use in your automated execution.
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/csv/010-csv.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "CSV"
4 | description: "CSV or Comma Separated Values is a popular format for data interchange and is supported by almost every table editing and spreadsheet tool. AnyWayData.com allows importing, editing and exporting CSV files also conversion to Markdown and other formats."
5 | ---
6 |
7 | CSV or Comma Separated Values is a popular format for data interchange and is supported by almost every table editing and spreadsheet tool. AnyWayData.com allows importing, editing and exporting CSV files also conversion to Markdown and other formats.
8 |
9 | ## What is CSV
10 |
11 | CSV stands for Comma Separated Values. It is one of the earliest data interchange formats and popularized by spreadsheet tools.
12 |
13 | A CSV file consists of an optional header line followed by lines of data. Each column is separated by a "," e.g.
14 |
15 | ```
16 | "Country","Population","World Share"
17 | "China","1,439,323,776","18.47 %"
18 | "India","1,380,004,385","17.70 %"
19 | "United States","331,002,651","4.25 %"
20 | ```
21 |
22 | Because the values can themselves contain "," the data values are often wrapped in quotes e.g. `"`
23 |
24 | And because the quoted values can contain `"` there has to be a convention for 'escaping' the quote value and this is usually done by adding two quotes together e.g. `""`.
25 |
26 | Because of the complexity around making sure all the values are quoted properly CSV files can be painful to create by hand.
27 |
28 | ## Official Standard
29 |
30 | There is no official standard for CSV although the conventions described above have been in use for so long that most tools abide by them and support them.
31 |
32 | For more information on the format and standards see:
33 |
34 | - [Library of Congress CSV format archive](https://www.loc.gov/preservation/digital/formats/fdd/fdd000323.shtml)
35 | - [Wikipedia](https://en.wikipedia.org/wiki/Comma-separated_values)
36 |
37 |
38 | ## Support for CSV
39 |
40 | AnyWayData.com allows both import and export of CSV files.
41 |
42 | Allowing you to convert from CSV to any of the supported output formats like Markdown, JSON, Ascii-Text, etc.
43 |
44 | And you can edit any CSV file:
45 |
46 | - change values in the CSV file
47 | - re-order the columns in a CSV file
48 | - delete columns from the CSV file
49 | - delete rows from the CSV file
50 |
51 | We use the open source [papa parse](https://www.papaparse.com/) library for our delimited file input and output processing.
52 |
53 |
54 |
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-12-ascii-table.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: ascii-table-output
3 | title: Ascii Table Output
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-12T12:00
7 | ---
8 |
9 | Can now output as Ascii Tables.
10 |
11 |
12 |
13 | ## Ascii Tables
14 |
15 | Output Ascii Tables can be useful for writing tabular content in emails, code or generally making notes in text files.
16 |
17 | e.g.
18 |
19 | ```
20 | +----------+----------+----------------------------+
21 | | test | name | email |
22 | +----------+----------+----------------------------+
23 | | 41211470 | Cyril | Tianna_Kris@gmail.com |
24 | | 4199130 | Andreane | Isac.Mills92@gmail.com |
25 | | 9258363 | Mariam | Abner_Feeney82@hotmail.com |
26 | | 93931922 | Desiree | Josh48@yahoo.com |
27 | +----------+----------+----------------------------+
28 | ```
29 |
30 | _Note: Data in table above was generated by the faker test generation in the tool_
31 |
32 | > The schema used for the data table was:
33 | >
34 | ```
35 | test
36 | [0-9]{3,10}
37 | name
38 | name.firstName
39 | email
40 | internet.email
41 | ```
42 |
43 | ## Ascii Table Libraries
44 |
45 | There are a variety of libraries available to support in this, and I listed [a few in the docs](https://anywaydata.com/docs/misc/related_tools#ascii-tables)
46 |
47 | I initially tried [Ascii-Table](https://github.com/sorensen/ascii-table) this was very easy to import from a CDN and I was able to create a prototype very quickly. I thought it lacked some of the more advanced configuration I was looking for and didn't think I'd be able to support as many export formats as I'd like e.g. reStructuredText
48 |
49 | So I moved on to [Ascii-Table3](https://github.com/AllMightySauron/ascii-table3) which has the very useful feature of being able to create styles to set a lot of configuration very quickly, it also allows more detailed configuration of the borders for the table.
50 |
51 | The problem here was that Ascii-Table3 is really designed for server side usage and has a requirement on a file system library to import styles. To get something working quickly I amended the import mechanism to allow it to work in the browser. This is an unofficial port with the code being available in the [github repo](https://github.com/eviltester/grid-table-editor/tree/master/js/libs/ascii-table3).
52 |
53 | The styles feature of Ascii-Table3 allowed me to add a drop down on the GUI to select different styles of tables without too much development effort.
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/tests/data_generation/unit/faker/fakerTestDataGenerator.test.js:
--------------------------------------------------------------------------------
1 | import { TestDataRule } from '../../../../js/data_generation/testDataRule.js';
2 | import {FakerTestDataGenerator} from '../../../../js/data_generation/faker/fakerTestDataGenerator.js';
3 | import { faker } from '@faker-js/faker';
4 |
5 |
6 | describe("Can generate Random Data using Faker",()=>{
7 |
8 | describe("Valid rules generate data",()=>{
9 | test('can generate from simple direct faker api command', () => {
10 |
11 | const fakerGenerator = new FakerTestDataGenerator(faker);
12 |
13 | const rule = new TestDataRule("Test", "internet.email");
14 | rule.type="faker";
15 |
16 | const myData = fakerGenerator.generateFrom(rule);
17 |
18 | expect(myData.data.length > 0).toBe(true);
19 | expect(myData.data.includes("@")).toBe(true);
20 | });
21 |
22 | test('can generate with a faker api that is a function with arguments', () => {
23 | const rule = new TestDataRule("Test", "string.alpha(5)");
24 | rule.type="faker";
25 | const myData = new FakerTestDataGenerator(faker).generateFrom(rule);
26 | expect(myData.data.length).toBe(5);
27 | });
28 |
29 | test('can generate with a function which is prefixed with faker', () => {
30 | const rule = new TestDataRule("Test", "faker.string.alpha(4)");
31 | rule.type="faker";
32 | const myData = new FakerTestDataGenerator(faker).generateFrom(rule);
33 | expect(myData.data.length).toBe(4);
34 | });
35 |
36 | test('can use mustache helper with functions', () => {
37 | const rule = new TestDataRule("Test", "helpers.mustache('{{word}}',{word:()=>`${this.string.alpha(15)}`})");
38 | rule.type="faker";
39 | const myData = new FakerTestDataGenerator(faker).generateFrom(rule);
40 | expect(myData.data.length).toBe(15);
41 | });
42 |
43 | });
44 |
45 | describe("Invalid rules generate error response",()=>{
46 | test('can determine a invalid TestDataRule', () => {
47 | const rule = new TestDataRule("Test", "internet.ea");
48 | rule.type="faker";
49 |
50 | const result = new FakerTestDataGenerator(faker).generateFrom(rule);
51 | expect(result.isError).toBe(true);
52 | expect(result.errorMessage).toBe("Could not find Faker API Command internet.ea {internet.ea}");
53 |
54 | });
55 | });
56 | });
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/json/010-json.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "JSON"
4 | description: "JSON is a data representation which is easy for humans to manage and can be processed as Javascript or as data for other languages. You can import, edit and export JSON object arrays and convert to CSV and other formats."
5 | ---
6 |
7 | JSON is a data object transformation standard based on Javascript but it is a language independent data format supported by multiple languages.
8 |
9 | ## What is JSON?
10 |
11 | JSON stands for Javascript Object Notation and is a human readable data format.
12 |
13 | e.g.
14 |
15 | ```
16 | [
17 | {
18 | "user": "Jesse.Bradtke97",
19 | "name": "Corine"
20 | },
21 | {
22 | "user": "Cielo.Little",
23 | "name": "Zander"
24 | }
25 | ]
26 | ```
27 |
28 | In the example above we can see two objects, these are collections of named properties and delimited by "{}"
29 |
30 | The objects have the properties 'user' and 'name'.
31 |
32 | The properties are key value pairs separated by ":".
33 |
34 | Each property key/value pair is separated by ","
35 |
36 | All the values in JSON are quoted either with single quotes or double quotes as used in the example.
37 |
38 | Multiple objects are collected in an array, represented by "[]" and each value in the array is separated by ",".
39 |
40 | Rather than having a 'header' with the column names, each property key becomes a header name.
41 |
42 | ## How is JSON different from Javascript?
43 |
44 | A JSON object is actually a subset of Javascript so can be used directly in a Javascript application.
45 |
46 | The main difference between JSON and Javascript is that the keys in JSON must be in double quotes, in Javascript they don't need to be double quotes, but then they must be valid Javascript identifiers i.e. they only use characters supported by Javascript.
47 |
48 | Also JSON is an object representation, not a programming language so it can not have functions, where as the 'value' in Javascript can be a function.
49 |
50 | JSON itself is a subset of Javascript so each valid JSON value is a valid Javascript object.
51 |
52 | ## AnyWayData Support for JSON
53 |
54 | AnyWayData can import and export JSON, and can then convert JSON to any of the supported formats e.g. Markdown, CSV, Delimited, Javascript, Gherkin and HTML.
55 |
56 | AnyWayData expects the JSON to be well formed and for each object in the imported array to have the same property names.
57 |
58 | The first object in the array is used to define the column headers so if later objects have additional properties they will be ignored.
--------------------------------------------------------------------------------
/js/grid/guarded-column-edits.js:
--------------------------------------------------------------------------------
1 | class GuardedColumnEdits{
2 |
3 | constructor(gridExtension) {
4 | this.gridExtras = gridExtension;
5 | }
6 |
7 | // todo: ids here look suspiciously ag-grid specific
8 |
9 | renameColId(id) {
10 | var editColDef = this.gridExtras.getColumnDef(id);
11 | var colTitle = prompt('Column Name?', editColDef.headerName);
12 |
13 | if (colTitle != null && colTitle != '') {
14 | console.log('rename column ' + id + ' with this name: ' + colTitle);
15 | } else {
16 | return false;
17 | }
18 |
19 | if(this.gridExtras.nameAlreadyExists(colTitle)){
20 | alert(`A column with name ${colTitle} already exists`);
21 | return false;
22 | }
23 |
24 | this.gridExtras.renameColId(id, colTitle);
25 | }
26 |
27 | deleteColId(id) {
28 |
29 | if(this.gridExtras.getNumberOfColumns()==1){
30 | alert("Cannot Delete The Only Column");
31 | return;
32 | }
33 |
34 | let editColDef = this.gridExtras.getColumnDef(id);
35 |
36 | if(!confirm('Are you Sure You Want to Delete Column Named '+ editColDef.headerName + "?"))
37 | return;
38 |
39 | this.gridExtras.deleteColumnId(id);
40 | }
41 |
42 |
43 | duplicateColumnId(position, id) {
44 | let colTitle = prompt('Copy Column As?');
45 |
46 | if (colTitle != null && colTitle != '') {
47 | console.log('duplicate a column with this name: ' + colTitle);
48 | } else {
49 | return;
50 | }
51 |
52 | if(this.gridExtras.nameAlreadyExists(colTitle)){
53 | alert(`A column with name ${colTitle} already exists`);
54 | return false;
55 | }
56 |
57 | this.gridExtras.duplicateColumn(position, id, colTitle);
58 | }
59 |
60 | addNeighbourColumnId(position, id) {
61 | let colTitle = prompt('New Column Name?');
62 |
63 | if (colTitle != null && colTitle != '') {
64 | console.log('create a new neighbour column with this name: ' + colTitle);
65 | } else {
66 | return;
67 | }
68 |
69 | if(this.gridExtras.nameAlreadyExists(colTitle)){
70 | alert(`A column with name ${colTitle} already exists`);
71 | return false;
72 | }
73 |
74 | this.gridExtras.addNeighbourColumnId(position, id, colTitle);
75 | }
76 | }
77 |
78 | export {GuardedColumnEdits}
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-24-pretty-print-markdown-alignment.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: pretty-print-markdown-alignment
3 | title: Left, Center and Right Alignment in Pretty Print Markdown
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-23T23:00
7 | ---
8 |
9 | Markdown output will now use the global alignment setting to right align and center align cell values.
10 |
11 |
12 |
13 | I read a [blog post from Github](https://github.blog/2022-06-10-how-we-think-about-browsers/) about how they determine which features of browsers to use, maintain backwards compatibility and architect the system for cross-browser and multiple version support.
14 |
15 | I noticed that they had a Markdown table using the alignment settings for pretty printed output.
16 |
17 | I thought it looked pretty good so I amended the layout algorithms for Markdown output to support the alignment.
18 |
19 | ## Default Pretty Printing Markdown Tables
20 |
21 | Which means by default the pretty printed Markdown tables in [AnyWayData.com](https://AnyWayData.com) look like:
22 |
23 | ```
24 | |A Column|B Column|C Column|
25 | |--------|--------|--------|
26 | |a |a b |a b c |
27 | |a |a b |a b c |
28 | |a |a b |a b c |
29 | ```
30 |
31 | ## Left Aligned Cells Pretty Printing Markdown Table
32 |
33 | When the `Column Align` property is set to `left` align, then the alignment `:` is added to the table:
34 |
35 | ```
36 | |A Column|B Column|C Column|
37 | |:-------|:-------|:-------|
38 | |a |a b |a b c |
39 | |a |a b |a b c |
40 | |a |a b |a b c |
41 | ```
42 |
43 | ## Center Aligned Cells Pretty Printing Markdown Table
44 |
45 | When the `Column Align` property is set to `center` align, then the alignment `:---:` is added to the table:
46 |
47 | ```
48 | |A Column|B Column|C Column|
49 | |:------:|:------:|:------:|
50 | | a | a b | a b c |
51 | | a | a b | a b c |
52 | | a | a b | a b c |
53 | ```
54 |
55 | ## Right Aligned Cells Pretty Printing Markdown Table
56 |
57 | When the `Column Align` property is set to `right` align, then the alignment `---:` is added to the table:
58 |
59 | ```
60 | |A Column|B Column|C Column|
61 | |-------:|-------:|-------:|
62 | | a| a b| a b c|
63 | | a| a b| a b c|
64 | | a| a b| a b c|
65 | ```
66 |
67 | ## Pretty Printing Markdown Tables
68 |
69 | This seems to be a much 'prettier' way of representing the pretty-printed tables and when we add functionality in the future to control alignment on a column by column basis then this functionality will carry through.
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/delimited/010-delimited.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "Delimited"
4 | description: "Delimited, DSV, or Delimiter Separated Values can refer to any set of data with a delimiter e.g. Tab Delimited, Pipe Delimited or even Comma Delimited. You can import, edit and export any delimited file and convert to Markdown and other formats."
5 | ---
6 |
7 | Delimited, DSV, or Delimiter Separated Values can refer to any set of data with a delimiter e.g. Tab Delimited, Pipe Delimited or even Comma Delimited. You can import, edit and export any delimited file and convert to Markdown and other formats.
8 |
9 | ## What is a Delimited File
10 |
11 | A Delimited file is one where the data values are separated by some sort of delimiter e.g. Tab Delimited, or Pipe Delimited. Comma Delimited files are the most common delimiter in use but they are really just a special case of a Delimited file.
12 |
13 | CSV is one of the most common data interchange formats, but Tab delimited files are also frequently used.
14 |
15 | Tab delimited is often the interchange format used when copy and pasting between spreadsheets. So if you want to copy data out of AnyWayData.com and into a spreadsheet or grid view in some other tool then a Tab Delimited output will likely do the job.
16 |
17 | A delimited file consists of an optional header line followed by lines of data. Each column is separated by a chosen delimiter e.g. using `:`
18 |
19 | ```
20 | "Country":"Population":"World Share"
21 | "China":"1,439,323,776":"18.47 %"
22 | "India":"1,380,004,385":"17.70 %"
23 | "United States":"331,002,651":"4.25 %"
24 | ```
25 |
26 | Because the values can themselves contain the delimiter the data values are often wrapped in quotes e.g. `"`
27 |
28 | And because the quoted values can contain `"` there has to be a convention for 'escaping' the quote value and this is usually done by adding two quotes together e.g. `""`.
29 |
30 | Because of the complexity around making sure all the values are quoted properly delimited files can be painful to create by hand.
31 |
32 | ## Support for Delimited Files
33 |
34 | AnyWayData.com allows both import and export of delimited files.
35 |
36 | Allowing you to convert from Tab Delimited or any custom delimiter to any of the supported output formats like Markdown, JSON, Ascii-Text, etc.
37 |
38 | And you can edit any delimited file:
39 |
40 | - change values
41 | - re-order the columns
42 | - delete columns
43 | - delete rows
44 |
45 | We use the open source [papa parse](https://www.papaparse.com/) library for our delimited file input and output processing.
46 |
47 |
48 |
--------------------------------------------------------------------------------
/js/libs/ascii-table3/printable-characters.js:
--------------------------------------------------------------------------------
1 | // based on https://github.com/xpl/printable-characters/blob/master/printable-characters.js
2 | // https://github.com/xpl/printable-characters
3 |
4 | "use strict";
5 |
6 | const ansiEscapeCode = '[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]'
7 | , zeroWidthCharacterExceptNewline = '\u0000-\u0008\u000B-\u0019\u001b\u009b\u00ad\u200b\u2028\u2029\ufeff\ufe00-\ufe0f'
8 | , zeroWidthCharacter = '\n' + zeroWidthCharacterExceptNewline
9 | , zeroWidthCharactersExceptNewline = new RegExp ('(?:' + ansiEscapeCode + ')|[' + zeroWidthCharacterExceptNewline + ']', 'g')
10 | , zeroWidthCharacters = new RegExp ('(?:' + ansiEscapeCode + ')|[' + zeroWidthCharacter + ']', 'g')
11 | , partition = new RegExp ('((?:' + ansiEscapeCode + ')|[\t' + zeroWidthCharacter + '])?([^\t' + zeroWidthCharacter + ']*)', 'g')
12 |
13 | let printableChars = {
14 |
15 | zeroWidthCharacters,
16 |
17 | ansiEscapeCodes: new RegExp (ansiEscapeCode, 'g'),
18 |
19 | strlen: s => Array.from (s.replace (zeroWidthCharacters, '')).length, // Array.from solves the emoji problem as described here: http://blog.jonnew.com/posts/poo-dot-length-equals-two
20 |
21 | isBlank: s => s.replace (zeroWidthCharacters, '')
22 | .replace (/\s/g, '')
23 | .length === 0,
24 |
25 | blank: s => Array.from (s.replace (zeroWidthCharactersExceptNewline, '')) // Array.from solves the emoji problem as described here: http://blog.jonnew.com/posts/poo-dot-length-equals-two
26 | .map (x => ((x === '\t') || (x === '\n')) ? x : ' ')
27 | .join (''),
28 |
29 | partition (s) {
30 | for (var m, spans = []; (partition.lastIndex !== s.length) && (m = partition.exec (s));) { spans.push ([m[1] || '', m[2]]) }
31 | partition.lastIndex = 0 // reset
32 | return spans
33 | },
34 |
35 | first (s, n) {
36 |
37 | let result = '', length = 0
38 |
39 | for (const [nonPrintable, printable] of module.exports.partition (s)) {
40 | const text = Array.from (printable).slice (0, n - length) // Array.from solves the emoji problem as described here: http://blog.jonnew.com/posts/poo-dot-length-equals-two
41 | result += nonPrintable + text.join ('')
42 | length += text.length
43 | }
44 |
45 | return result
46 | }
47 | }
48 |
49 | const isBlank = printableChars.isBlank;
50 | const partitionf = printableChars.partition;
51 | const strlen = printableChars.strlen;
52 |
53 | export {isBlank, partitionf as partition, strlen}
--------------------------------------------------------------------------------
/.github/workflows/cli-deploy.yml:
--------------------------------------------------------------------------------
1 | name: create-bun-exe
2 | on:
3 | workflow_dispatch:
4 | inputs:
5 | tag:
6 | commit:
7 | default: "master"
8 | jobs:
9 | bun-build:
10 | name: build-bun-exes
11 | runs-on: ubuntu-latest
12 | defaults:
13 | run:
14 | working-directory: ./cli
15 | steps:
16 | - uses: actions/checkout@v4
17 | - uses: oven-sh/setup-bun@v2
18 | with:
19 | bun-version: 1.2.10
20 | - name: install dependencies
21 | run: bun install
22 | - name: build windows
23 | run: |
24 | bun build ./index.ts --compile --minify --sourcemap --target=bun-windows-x64 --outfile ${{ github.workspace }}/out/windows64/anywaydata.exe
25 | zip -j ${{ github.workspace }}/windows64.zip ${{ github.workspace }}/out/windows64/anywaydata.exe
26 | - name: build linux-x64
27 | run: |
28 | bun build ./index.ts --compile --target=bun-linux-x64 --outfile ${{ github.workspace }}/out/linux-x64/anywaydata
29 | zip -j ${{ github.workspace }}/linux-x64.zip ${{ github.workspace }}/out/linux-x64/anywaydata
30 | - name: build linux-arm64
31 | run: |
32 | bun build ./index.ts --compile --outfile ${{ github.workspace }}/out/linux-arm64/anywaydata --target=bun-linux-arm64
33 | zip -j ${{ github.workspace }}/linux-arm64.zip ${{ github.workspace }}/out/linux-arm64/anywaydata
34 | - name: build mac-x64
35 | run: |
36 | bun build ./index.ts --compile --outfile ${{ github.workspace }}/out/macos-x64/anywaydata --target=bun-darwin-x64
37 | zip -j ${{ github.workspace }}/macos-x64.zip ${{ github.workspace }}/out/macos-x64/anywaydata
38 | - name: build mac-arm64
39 | run: |
40 | bun build ./index.ts --compile --outfile ${{ github.workspace }}/out/macos-arm64/anywaydata --target=bun-darwin-arm64
41 | zip -j ${{ github.workspace }}/macos-arm64.zip ${{ github.workspace }}/out/macos-arm64/anywaydata
42 | - name: check dir
43 | run: ls -R ${{ github.workspace }}/out
44 | - uses: ncipollo/release-action@v1.16.0
45 | with:
46 | tag: ${{ inputs.tag }}
47 | commit: ${{ inputs.commit }}
48 | name: Draft Release ${{ github.ref }}
49 | draft: true
50 | artifacts: |
51 | ${{ github.workspace }}/windows64.zip
52 | ${{ github.workspace }}/linux-x64.zip
53 | ${{ github.workspace }}/linux-arm64.zip
54 | ${{ github.workspace }}/macos-x64.zip
55 | ${{ github.workspace }}/macos-arm64.zip
56 | body: 'AnyWayData CLI versions'
57 | allowUpdates: true
58 |
59 |
--------------------------------------------------------------------------------
/js/gui_components/exportControls.js:
--------------------------------------------------------------------------------
1 | import {Download} from './download.js';
2 |
3 | class ExportsPageMap{
4 |
5 | constructor(){
6 | this.fileDownloadButtonQuery = "#filedownload";
7 | this.markdownTextArea = "#markdownarea";
8 | this.copyTextButton = "#copyTextButton";
9 | }
10 | }
11 |
12 | class ExportControls {
13 |
14 | constructor(exporter) {
15 | this.pageMap = new ExportsPageMap();
16 | this.exporter = exporter;
17 | }
18 |
19 | addHooksToPage(container){
20 |
21 | var element = container.querySelector(this.pageMap.fileDownloadButtonQuery);
22 | var addDownloadButtonListener = this.fileDownload.bind(this);
23 | element.addEventListener('click', addDownloadButtonListener, false);
24 |
25 | var copyButton = container.querySelector(this.pageMap.copyTextButton);
26 | var copyTextButtonListener = this.copyText.bind(this);
27 | copyButton.addEventListener('click', copyTextButtonListener, false);
28 | }
29 |
30 | fileDownload(){
31 |
32 | this.renderTextFromGrid();
33 |
34 | var type = document.querySelector("li.active-type a").getAttribute("data-type");
35 |
36 | if(!this.exporter.canExport(type)){
37 | console.log(`Data Type ${type} not supported`)
38 | return;
39 | }
40 |
41 | var filename = "export" + this.exporter.getFileExtensionFor(type);
42 |
43 | var text = document.querySelector(this.pageMap.markdownTextArea).value;
44 | new Download(filename).downloadFile(text);
45 | }
46 |
47 | copyText() {
48 |
49 | var copyText = document.querySelector(this.pageMap.markdownTextArea);
50 |
51 | // select text
52 | copyText.select();
53 | copyText.setSelectionRange(0, 99999);
54 |
55 | document.execCommand("copy");
56 |
57 | let copyButton = document.querySelector(this.pageMap.copyTextButton);
58 | copyButton.innerText = "Copied";
59 | setTimeout(
60 | function(aButton){ aButton.innerText = "Copy"; }
61 | , 3000, copyButton);
62 | }
63 |
64 | renderTextFromGrid() {
65 |
66 | let type = document.querySelector("li.active-type a").getAttribute("data-type");
67 |
68 | if(!this.exporter.canExport(type)){
69 | console.log(`Data Type ${type} not supported for exporting`);
70 | return;
71 | }
72 |
73 | let textToRender = this.exporter.getGridAs(type);
74 | this.setTextFromString(textToRender);
75 | }
76 |
77 | setTextFromString(someText){
78 | document.getElementById('markdownarea').value = someText;
79 | }
80 | }
81 |
82 | export {ExportControls, ExportsPageMap}
--------------------------------------------------------------------------------
/docs-src/blog/2022-06-25-added-docs-for-markdown-ascii.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: docs-markdown-ascii
3 | title: Added Documentation for Markdown and Ascii
4 | authors: alan
5 | tags: [release]
6 | date: 2022-06-25T16:00
7 | ---
8 |
9 | We now have documentation for [Markdown](/docs/data-formats/markdown/markdown) and [Ascii Tables](/docs/data-formats/ascii-tables/ascii-tables)
10 |
11 |
12 |
13 | ## Added Markdown Documentation
14 |
15 | I'm starting to add more documentation to the tool.
16 |
17 | I've now documented the [options for Markdown output](/docs/data-formats/markdown/options) with some additional information as to [what Markdown is](/docs/data-formats/markdown/markdown)
18 |
19 | I personally use Markdown for everything.
20 |
21 | I write my daily logs in Markdown, all my [recent books](https://www.eviltester.com/page/books/) have been written in Markdown.
22 |
23 | I primarily use [pandoc](https://pandoc.org/) to convert long form Markdown files into PDF and that works very well for me.
24 |
25 | To preview markdown when I want to view it I either use the preview pane in [Visual Studio Code](https://code.visualstudio.com/docs/languages/markdown) although other plugins are available.
26 |
27 | When I want to review the content that I've written, I'll typically use [MarkdownLivePreview.com](https://markdownlivepreview.com/) and if you want to preview the tables that you create in AnyWayData.com then I recommend copy and pasting the Markdown generated here into the MarkdownLivePreview text pane. It handles tables very well and formats them nicely by default.
28 |
29 | MarkdownLivePreview also works well with the AnyWayData.com options to embolden or italicize headers and columns.
30 |
31 | ## Added Ascii Table Documentation
32 |
33 | I grew up in the age of text files so I'm used to seeing Ascii Tables to represent tabular formatted data.
34 |
35 | e.g.
36 |
37 | ```
38 | +---------------+---------------+-------------+
39 | | Country | Population | World Share |
40 | +---------------+---------------+-------------+
41 | | China | 1,439,323,776 | 18.47 % |
42 | | India | 1,380,004,385 | 17.70 % |
43 | | United States | 331,002,651 | 4.25 % |
44 | +---------------+---------------+-------------+
45 | ```
46 |
47 | The Ascii conversion used in AnyWayData.com is provided by the open-source [Ascii Table 3](https://www.npmjs.com/package/ascii-table3) library.
48 |
49 | I chose this because of the numerous out of the box styles which I could use as [styling options for the Ascii Table](/docs/data-formats/ascii-tables/options) conversion.
50 |
51 | I had to make a few tweaks to the library code to get it working in the browser but I haven't expanded or changed any of the styles yet.
52 |
53 |
54 |
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/gherkin/options.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: "Gherkin Options"
4 | description: "Options available for pretty printing the Gherkin output suitable for using in an executable specification."
5 | ---
6 |
7 | Options available for pretty printing the Gherkin output suitable for using in an executable specification.
8 |
9 | ## In Cell Padding
10 |
11 | The `In Cell Padding` option determines if the values in the table will have padding to make them more readable.
12 |
13 | Options are:
14 |
15 | - None
16 | - Left
17 | - Right
18 | - Both
19 |
20 | ### None
21 |
22 | ```
23 | |Country|Population|World Share|
24 | |China|1,439,323,776|18.47 %|
25 | |India|1,380,004,385|17.70 %|
26 | |United States|331,002,651|4.25 %|
27 | ```
28 |
29 | ### Left
30 |
31 | ```
32 | | Country| Population| World Share|
33 | | China| 1,439,323,776| 18.47 %|
34 | | India| 1,380,004,385| 17.70 %|
35 | | United States| 331,002,651| 4.25 %|
36 | ```
37 |
38 | ### Right
39 |
40 | ```
41 | |Country |Population |World Share |
42 | |China |1,439,323,776 |18.47 % |
43 | |India |1,380,004,385 |17.70 % |
44 | |United States |331,002,651 |4.25 % |
45 | ```
46 |
47 | ### Both
48 |
49 | ```
50 | | Country | Population | World Share |
51 | | China | 1,439,323,776 | 18.47 % |
52 | | India | 1,380,004,385 | 17.70 % |
53 | | United States | 331,002,651 | 4.25 % |
54 | ```
55 |
56 | ## Pretty Print
57 |
58 | `Pretty Print` will format the cells in the table so they fit the widest value in the column.
59 |
60 | ```
61 | |Country |Population |World Share|
62 | |China |1,439,323,776|18.47 % |
63 | |India |1,380,004,385|17.70 % |
64 | |United States|331,002,651 |4.25 % |
65 | ```
66 |
67 | ## Show Headers
68 |
69 | `Show Headers` is the configuration that controls if the Headers are shown in the table or not.
70 |
71 | ```
72 | |China|1,439,323,776|18.47 %|
73 | |India|1,380,004,385|17.70 %|
74 | |United States|331,002,651|4.25 %|
75 | ```
76 |
77 | ## Left Indent
78 |
79 | `Left Indent` is the string that will be displayed to the left of each column, in the example below I added three spaces into the text field ` `.
80 |
81 | ```
82 | |China|1,439,323,776|18.47 %|
83 | |India|1,380,004,385|17.70 %|
84 | |United States|331,002,651|4.25 %|
85 | ```
86 |
87 | ## Combine the Options
88 |
89 | It is possible to combine the options to achieve the desired results.
90 |
91 | e.g. adding 2 spaces, in front, with padding on both sides, showing headers and pretty printing:
92 |
93 | ```
94 | | Country | Population | World Share |
95 | | China | 1,439,323,776 | 18.47 % |
96 | | India | 1,380,004,385 | 17.70 % |
97 | | United States | 331,002,651 | 4.25 % |
98 | ```
--------------------------------------------------------------------------------
/docs-src/src/components/HomepageFeatures/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import styles from './styles.module.css';
4 |
5 | const FeatureList = [
6 | {
7 | title: 'Easy to Use',
8 | //Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
9 | description: (
10 | <>
11 | The Data Table Editor is designed to be easy to use and
12 | uses AG Grid's Community Edition Data Grid to create an intuitive
13 | editing experience.
14 |
15 | You can create and edit data in the grid and "Set Test From Grid"
16 | to view data in your chosen format. Or paste data into the text area
17 | and "Set Grid From Text". You can even import and export from files.
18 | >
19 | ),
20 | },
21 | {
22 | title: 'Import and Export Data',
23 | //Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
24 | description: (
25 | <>
26 | Any Data Grid of data can be converted to multiple table formats e.g.
27 | Markdown, CSV, Gherkin, JSON and plain HTML. These formats can all be
28 | exported to and converted between. It is possible to import Markdown,
29 | CSV, Gherkin and JSON for editing the data.
30 | >
31 | ),
32 | },
33 | {
34 | title: 'Random Data Generation',
35 | //Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
36 | description: (
37 | <>
38 | You can Generate random data in a Data Grid to use for testing. Simply
39 | configure a test data schema and choose from predefined random formats or
40 | write your field value description as a RegEx. Generate as many rows as your
41 | computer can handle. Want 1000 records? Easy. 100,000? Easy. 1,000,000 sure,
42 | it might take 30 seconds or so, but if your computer can handle it, we can
43 | generate it.
44 | >
45 | ),
46 | },
47 | ];
48 |
49 | //
50 | function Feature({Svg, title, description}) {
51 | return (
52 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/js/gui_components/data-grid-editor/ag-grid/main-display-grid.js:
--------------------------------------------------------------------------------
1 | import { GridExtension } from "./gridExtension-ag-grid.js";
2 | import { GridControl, GridControlsPageMap } from "../gridControl.js"
3 | import { CustomHeaderAgGrid } from "./customHeader-ag-grid.js";
4 |
5 | /*
6 | Grid Features Used:
7 | - custom column header to add buttons for add new etc.
8 | - in cell editing
9 | - user row moving - drag drop
10 | - user column dragging - left right
11 | - column sorting
12 | - table filtering
13 | - row select (for deleting row, and adding above below)
14 | */
15 |
16 |
17 | class ExtendedDataGrid {
18 |
19 | constructor() {
20 | var rowData = [];
21 |
22 | var columnDefs = [
23 | {
24 | headerName: '~rename-me',
25 | field: 'column1'
26 | }
27 | ];
28 |
29 | this.gridOptions = {
30 | columnDefs: columnDefs,
31 | rowData: rowData,
32 |
33 | defaultColDef: {
34 | wrapText: true,
35 | autoHeight: true,
36 | resizable: true,
37 | rowDrag: true,
38 | editable: true,
39 | filter:true,
40 | sortable:true,
41 | },
42 |
43 | components: {
44 | agColumnHeader: CustomHeaderAgGrid,
45 | },
46 |
47 | rowDragManaged: true,
48 | rowDragMultiRow: true,
49 | rowSelection: {
50 | mode: 'multiRow',
51 | checkboxes: false,
52 | headerCheckbox: false,
53 | enableClickSelection: true,
54 | },
55 | //onColumnResized: (params) => {params.api.resetRowHeights();}
56 | };
57 | }
58 |
59 | createChildGrid(parentGridDiv){
60 |
61 | let gridControls = new GridControl(new GridControlsPageMap());
62 | gridControls.addGuiIn(parentGridDiv)
63 |
64 | let gridDiv = document.querySelector('#myGrid');
65 |
66 | this.gridApi = agGrid.createGrid(gridDiv, this.gridOptions);
67 |
68 | // having setup the grid, make it a little more responsive - removed when adding tippy as it need doctype html
69 | // TODO: add some resizing div controls
70 | //setTimeout(function(gridDiv){gridDiv.style.height="40%";},1000, gridDiv);
71 |
72 | this.gridExtras = new GridExtension(this.gridApi);
73 |
74 | gridControls.useThisGridFunctionality(this.gridExtras);
75 | gridControls.addHooksToPage(document);
76 | }
77 |
78 | sizeColumnsToFit(){
79 | this.gridApi.sizeColumnsToFit();
80 | }
81 |
82 | getGridExtras(){
83 | return this.gridExtras;
84 | }
85 | }
86 |
87 | export {ExtendedDataGrid};
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/javascript/010-javascript.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "Javascript"
4 | description: "Javascript is a programming language, a subset of which can be used to represent data objects and arrays. You can import, edit and export Javascript object arrays and convert to CSV and other formats."
5 | ---
6 |
7 | Javascript is a programming language, the AnyWayData Javascript option is likely only to be of use to Javascript programmers.
8 |
9 | ## What is Javascript?
10 |
11 | Javascript is a programming language, but a subset of Javascript can be used to represent data objects.
12 |
13 | e.g.
14 |
15 | ```
16 | [
17 | {
18 | user: "Jesse.Bradtke97",
19 | name: "Corine"
20 | },
21 | {
22 | user: "Cielo.Little",
23 | name: "Zander"
24 | }
25 | ]
26 | ```
27 |
28 | In the example above we can see two objects, these are collections of named properties and delimited by "{}"
29 |
30 | The objects have the properties 'user' and 'name'.
31 |
32 | The properties are key value pairs separated by ":".
33 |
34 | Each property key/value pair is separated by ","
35 |
36 | In Javascript the key values do not need to be enclosed in quotes. Although it is perfectly valid to do that. When key values are enclosed in quotes we have actually written [JSON](/docs/data-formats/json/json)
37 |
38 | Multiple objects are collected in an array, represented by "[]" and each value in the array is separated by ",".
39 |
40 | Rather than having a 'header' with the column names, each property key becomes a header name.
41 |
42 | ## How is Javascript different from JSON?
43 |
44 | A JSON object is actually a subset of Javascript so can be used directly in a Javascript application.
45 |
46 | Javascript is a programming language so Javascript data objects are not used to exchange data between applications.
47 |
48 | The main difference between JSON and Javascript is that the keys in JSON must be in double quotes, in Javascript they don't need to be double quotes, but then they must be valid Javascript identifiers i.e. they only use characters supported by Javascript.
49 |
50 | In Javascript the values can be functions but importing function values is not supported by AnyWayData.
51 |
52 | ## AnyWayData Support for Javascript
53 |
54 | AnyWayData can import and export Javascript, and can then convert Javascript to any of the supported formats e.g. Markdown, CSV, Delimited, Javascript, Gherkin and HTML.
55 |
56 | AnyWayData expects the Javascript to be well formed and for each object in the imported array to have the same property names.
57 |
58 | The first object in the array is used to define the column headers so if later objects have additional properties they will be ignored.
59 |
60 | Not all formatting and spacing will be supported by AnyWayData so you may need to minimize the Javascript before importing.
--------------------------------------------------------------------------------
/js/data_generation/rulesBasedDataGenerator.js:
--------------------------------------------------------------------------------
1 | import {FakerTestDataGenerator} from "./faker/fakerTestDataGenerator.js";
2 | import {RegexTestDataGenerator} from "./regex/regexTestDataGenerator.js";
3 | import { LiteralTestDataGenerator } from "./literal/literalTestDataGenerator.js";
4 | import { dataResponse } from "./ruleResponse.js";
5 |
6 | export class RulesBasedDataGenerator{
7 |
8 | constructor(aFaker, RandExp){
9 | this.faker = aFaker;
10 | this.RandExp = RandExp;
11 |
12 | this.fakerGenerator = new FakerTestDataGenerator(aFaker);
13 | this.regexGenerator = new RegexTestDataGenerator(RandExp);
14 | this.literalGenerator = new LiteralTestDataGenerator();
15 | this.defaultGenerator = new DefaultTestDataGenerator();
16 | }
17 |
18 | generateFromRules(thisMany, fromRules){
19 |
20 | // given some rules
21 | // generate thisMany instances
22 | // data is row of values where the first row is the headers
23 | const data = [];
24 |
25 | const headers = fromRules.map((rule) => rule.name);
26 | data.push(headers);
27 |
28 | for(var row=0; row {
38 |
39 | var dataGen = "";
40 | var generator;
41 | var value;
42 |
43 | switch (rule.type) {
44 | case "faker": // is faker?
45 | generator = this.fakerGenerator;
46 | break;
47 |
48 | case "regex":
49 | generator = this.regexGenerator;
50 | break;
51 |
52 | case "literal":
53 | generator = this.literalGenerator;
54 | break;
55 |
56 | default:
57 | console.warn(`${rule.name} has Unidentified rule type ${rule.type} with spec ${rule.ruleSpec}`);
58 | generator = this.defaultGenerator;
59 | }
60 |
61 | value = generator.generateFrom(rule);
62 |
63 | if(value.isError){
64 | dataGen = "**ERROR**";
65 | }else{
66 | dataGen = value.data;
67 | }
68 |
69 | return dataGen;
70 | });
71 |
72 | return aRow;
73 | }
74 | }
75 |
76 | class DefaultTestDataGenerator{
77 |
78 | constructor(){
79 | }
80 |
81 | generateFrom(aRule){
82 | return dataResponse(aRule.ruleSpec);
83 | }
84 | }
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/markdown/010-markdown.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "Markdown Tables"
4 | description: "Markdown is a popular format for converting to HTML or PDF, Markdown tables are not easy to edit or format in a normal text editor, but AnyWayData.com makes working with Markdown tables simple."
5 | ---
6 |
7 | ## What is Markdown
8 |
9 | Markdown is a text representation which can be easily read and created by humans, and is easy to convert to other formats for publication like pdf or html.
10 |
11 | For example, the following text is a simple Markdown document with headings and text in bold.
12 |
13 | ```
14 | # This is a Heading 1
15 |
16 | This is a paragraph of text.
17 |
18 | ## This is a Sub Heading
19 |
20 | And **this text is in bold.**
21 | ```
22 |
23 | Markdown supports:
24 |
25 | - links `[link text](url to link to)`
26 | - italics `_italics_`
27 | - horizontal rules `---`
28 | - images ``
29 |
30 | For a full description of Markdown format and syntax we recommend using [MarkdownGuide.org](https://www.markdownguide.org/).
31 |
32 | ## Markdown Tables
33 |
34 | Most of the syntax for a Markdown document is very simple, the challenge comes with you want to create a table of data.
35 |
36 | For example, a simple Markdown table would look like:
37 |
38 | ```
39 | |Column A|Column B|Column C|
40 | |-----|-----|-----|
41 | |a|a b|a b c|
42 | |a|a b|a b c|
43 | |a|a b|a b c|
44 | ```
45 |
46 | There is a Heading row, the heading delimiter row, followed by rows of data.
47 |
48 | This is often difficult to work with because of the need to add "|" symbols to represent columns.
49 |
50 | With a large table this becomes very tricky.
51 |
52 | Also, since Markdown is supposed to be human readable, we would ideally want the Markdown Tables to be well formatted, but this is difficult to do by hand.
53 |
54 | e.g. a pretty printed and well formatted Markdown Table.
55 |
56 | ```
57 | | Column A | Column B | Column C |
58 | | -------- | -------- | -------- |
59 | | a | a b | a b c |
60 | | a | a b | a b c |
61 | | a | a b | a b c |
62 | ```
63 |
64 | ## Converting Markdown to HTML
65 |
66 | There are many tools available to convert Markdown to HTML.
67 |
68 | Online tools we recommend for working online with Markdown, to convert to other formats are:
69 |
70 | - [MarkdownLivePreview.com](https://markdowntohtml.com/) to quickly convert Markdown into a reviewable format.
71 | - [MarkdownToHtml.com](https://markdowntohtml.com/) to quickly convert to HTML
72 |
73 | We have a list of other tools for working with Markdown in our [Markdown Previewers and Editors](/docs/misc/related_tools#markdown-previewers-and-editors) reference section.
74 |
75 | ## Working With Markdown in AnyWayData.com
76 |
77 | AnyWayData.com supports importing from, and exporting to, Markdown formatted tables.
78 |
--------------------------------------------------------------------------------
/tests/data_formats/generic-data-table.test.js:
--------------------------------------------------------------------------------
1 | import {GenericDataTable} from '../../js/data_formats/generic-data-table';
2 |
3 | describe("Can create Generic Data Table from scratch", ()=>{
4 |
5 | test('create empty table', () => {
6 | let dataTable = new GenericDataTable();
7 | expect(dataTable.getHeaders().length).toBe(0);
8 | expect(dataTable.getRowCount()).toBe(0);
9 | });
10 |
11 | test('can add headers', () => {
12 | let dataTable = new GenericDataTable();
13 | expect(dataTable.getColumnCount()).toBe(0);
14 | dataTable.addHeader("header 1");
15 | expect(dataTable.getColumnCount()).toBe(1);
16 | dataTable.addHeader("header 2");
17 | expect(dataTable.getColumnCount()).toBe(2);
18 | expect(dataTable.getHeader(1)).toBe("header 2");
19 | });
20 |
21 | test('can add headers in bulk', () => {
22 | let dataTable = new GenericDataTable();
23 |
24 | dataTable.setHeaders(["header 1", "header 2"]);
25 | expect(dataTable.getColumnCount()).toBe(2);
26 | expect(dataTable.getHeader(0)).toBe("header 1");
27 | expect(dataTable.getHeader(1)).toBe("header 2");
28 | });
29 |
30 | test('can get headers in bulk', () => {
31 | let dataTable = new GenericDataTable();
32 |
33 | dataTable.setHeaders(["header 1", "header 2"]);
34 | expect(dataTable.getHeaders()[0]).toBe("header 1");
35 | expect(dataTable.getHeaders()[1]).toBe("header 2");
36 | });
37 |
38 | test('can get rows as objects', () => {
39 | let dataTable = new GenericDataTable();
40 |
41 | dataTable.setHeaders(["header 1", "header2"]);
42 | dataTable.appendDataRow(["bob","eris"]);
43 | dataTable.appendDataRow(["aleister","william"]);
44 |
45 | let aRow = dataTable.getRowAsObject(0);
46 | expect(aRow["header 1"]).toBe("bob");
47 | expect(aRow.header2).toBe("eris");
48 |
49 | let anotherRow = dataTable.getRowAsObjectUsingHeadings(1,["field1","field2"]);
50 | expect(anotherRow["field1"]).toBe("aleister");
51 | expect(anotherRow.field2).toBe("william");
52 |
53 | });
54 |
55 | test('can clear table', () => {
56 | let dataTable = new GenericDataTable();
57 |
58 | dataTable.setHeaders(["header 1", "header2"]);
59 | dataTable.appendDataRow(["bob","eris"]);
60 | dataTable.appendDataRow(["aleister","william"]);
61 |
62 | dataTable.clear();
63 | expect(dataTable.getHeaders().length).toBe(0);
64 | expect(dataTable.getRowCount()).toBe(0);
65 | });
66 |
67 | });
68 |
69 |
70 |
71 | /*
72 |
73 | TODO : unsupported scenarios
74 |
75 | - add header after rows have been added and expand rows - expectation is that headers are set prior to adding data
76 | - setting cells individually, expectation is that this is a bulk operation adding a row at a time with appendDataRow
77 |
78 | */
--------------------------------------------------------------------------------
/js/data_formats/generic-data-table-utils.js:
--------------------------------------------------------------------------------
1 | /*
2 | Helper class to set GenericDataTable data from various formats
3 | */
4 | class GenericDataTableUtils {
5 |
6 | setGenericDataTableFromDataArray(aGenericDataTable, aDataArray){
7 | // first row are headings
8 | // rest are data
9 | if(aDataArray===undefined){
10 | return true;
11 | }
12 | if(aDataArray===null){
13 | // clear the table
14 | return aGenericDataTable.clear();
15 | }
16 | if(aDataArray.length == 0){
17 | return false;
18 | }
19 |
20 | // process headers
21 | const dataHeaders = aDataArray[0];
22 | if(dataHeaders===undefined){
23 | // do not change the headers
24 | }else{
25 | if(dataHeaders===null){
26 | // bad idea to try and set headers to null
27 | // leave data as it is
28 | console.log("Cannot set headers to null, data unchanged");
29 | return false;
30 | }
31 | if(dataHeaders.length==0){
32 | // what are you doing? table needs headers
33 | // leave data as it is
34 | console.log("Cannot set headers to none, data unchanged");
35 | return false;
36 | }
37 | }
38 |
39 | this.headers = [];
40 | for(let header of dataHeaders){
41 | // add header value to the grid
42 | aGenericDataTable.addHeader(header);
43 | }
44 |
45 | // todo: consider, rather than copying array we remove header line and use the array as the data
46 | // or have an index offset and keep header where it is e.g. GenericDataTableHeaderArrayBacked
47 | this.rows=[];
48 | // process data
49 | for( let rowId = 1; rowId {
83 | let rowData = [];
84 | headers.forEach(header => {
85 | let cellValue = row[header];
86 | if(cellValue===undefined){
87 | cellValue="";
88 | }
89 | rowData.push(cellValue);
90 | });
91 | aGenericDataTable.appendDataRow(rowData);
92 | });
93 |
94 | return true;
95 | }
96 | }
97 |
98 | export {GenericDataTableUtils}
--------------------------------------------------------------------------------
/docs-src/docs/010-intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: About
4 | ---
5 |
6 | # About Data Table Editor and Generator
7 |
8 | A Simple Data Table Editor and Test Data Generator.
9 |
10 | Free to use and Open source.
11 |
12 | ## Main Editor Features:
13 |
14 | - Load Data into an editable Data Grid
15 | - Sort and Filter Data in the grid
16 | - Filter globally across all fields
17 | - Edit data in the grid itself
18 | - Add new columns
19 | - Rename Columns
20 | - Re-order columns
21 | - Re-order rows
22 | - Export Data to files
23 | - Add new rows to the table
24 | - Delete selected rows from the table
25 | - Edit as text and import into the data grid
26 |
27 | ## Import/Output Features:
28 |
29 | - Import Markdown Tables for editing
30 | - Import CSV files for editing
31 | - Import JSON files for editing
32 | - Import Gherkin tables for editing
33 | - Export Data Grid as Markdown
34 | - Export Data Grid as CSV
35 | - Export Data Grid as JSON
36 | - Export Data Grid as Gherkin Table format
37 | - Export Data Grid as HTML
38 | - Export Data as File
39 | - Export Data to Clipboard
40 | - Drag and Drop files to import data
41 |
42 | ## Test Data Generation Features
43 |
44 | - Define Columns in an editable Grid
45 | - Define Test Data as Regular Expressions
46 | - Define Test Data using Faker.js functions
47 | - No limit to the number of rows you can generate
48 |
49 |
50 | ## CLI Version - Windows, Mac, Linux
51 |
52 | An experimental CLI version is available to download from the [releases page on Github](https://github.com/eviltester/grid-table-editor/releases).
53 |
54 | Currently this implements the Test Data Generation using the same Test Data Spec format as the web version and outputs to CSV format.
55 |
56 | - [Downloads for Windows, Mac, Linux](https://github.com/eviltester/grid-table-editor/releases)
57 |
58 | e.g. given an input file called `faker-regex.txt`
59 |
60 | ```
61 | Company
62 | company.name
63 | Regex Generated Field
64 | [A-Z]{3,6}[0-9]{0,6}
65 | ```
66 |
67 | The following command would generate 3000 records into `output.txt`
68 |
69 | ```
70 | ./anywaydata generate –i ./company.txt -n 3000 –o output.txt
71 | ```
72 |
73 | Example output:
74 |
75 | ```
76 | "Company","Regex Generated Field"
77 | "Jones and Sons","QZRGC"
78 | "Kunze, Morissette and Daniel","YMHV89"
79 | "Cole, Padberg and Cronin","QGG"
80 | ```
81 |
82 | ### CLI on Mac
83 |
84 | For Mac you will need to give the application permissions to run.
85 |
86 | Allow it to run – either from `System Settings > Privacy & Security`
87 |
88 | Or:
89 |
90 | ```
91 | xattr -dr com.apple.quarantine "/path/to/your/app.app/Contents/MacOS/executable"
92 | ```
93 |
94 | e.g. `xattr -dr com.apple.quarantine "$(pwd)/anywaydata"`
95 |
96 | Set as executable in command line:
97 |
98 | ```
99 | chmod +x anywaydata
100 | ```
101 |
102 | ---
103 |
104 | Built by [Alan Richardson](https://eviltester.com)
105 |
106 | Using:
107 |
108 | - [AG Grid](https://ag-grid.com) Community Edition
109 | - [RandExp.js](http://fent.github.io/randexp.js/)
110 | - [Faker.js](https://fakerjs.dev/)
111 | - [PapaParse](https://www.papaparse.com/)
112 |
113 | [[github source](https://github.com/eviltester/grid-table-editor)]
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/javascript/options.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: "Javascript Options"
4 | description: "Options available for converting to Javascript in AnyWayData.com. This includes formatting numbers, pretty print control and representing as a named array property."
5 | ---
6 |
7 | The configuration options for Javascript are listed below. These are similar to [JSON](/docs/data-formats/json/json).
8 |
9 | ## Number Convert
10 |
11 | The `Number Convert` option detects if the 'value' is a number and if so will be output without quotes.
12 |
13 | e.g. with `Number Convert` off
14 |
15 | ```
16 | [
17 | {
18 | name: "Monica",
19 | age: "29"
20 | }
21 | ]
22 | ```
23 |
24 | And with `Number Convert` on:
25 |
26 | ```
27 | [
28 | {
29 | name: "Monica",
30 | age: 29
31 | }
32 | ]
33 | ```
34 |
35 | ## Pretty Print
36 |
37 | The `Pretty Print` option controls the pretty printing of Javascript.
38 |
39 | With no `Pretty Print` the Javascript will be minified:
40 |
41 | ```
42 | [{name:"Monica",age:"29"}]
43 | ```
44 |
45 | With `Pretty Print` enabled the Javascript will be generated with the chosen `Delimiter`.
46 |
47 | e.g.
48 |
49 | ```
50 | [
51 | {
52 | name: "Monica",
53 | age: "29"
54 | }
55 | ]
56 | ```
57 |
58 | ## Delimiter
59 |
60 | The `Delmiter` is the value used to space out the Javascript indentation. This is only used when `Pretty Print` has been set to 'on'.
61 |
62 | The `Delimiter` drop down allows you to choose some standard values:
63 |
64 | - `Tab [\t]` - use tabs to indent the values
65 | - `Space [ ]` - use a single space to indent the values
66 | - `Custom Value` - use the value in the `Custom` Text box.
67 |
68 | ## Custom
69 |
70 | The `Custom` text box allows you to add any indentation string to use during the `Pretty Print` formatting.
71 |
72 | This would normally be used to add spaces e.g. ` ` two spaces or ` ` three spaces.
73 |
74 | It is possible to use this text area with strings like "..." to generate:
75 |
76 | ```
77 | [
78 | ...{
79 | ......name: "Monica",
80 | ......age: "29"
81 | ...}
82 | ]
83 | ```
84 |
85 | This would generate invalid Javascript but might be useful for some publications or examples.
86 |
87 | When the `Custom` value is a number e.g. `4` then the indentation will use four spaces i.e. ` `
88 |
89 | ## As Object
90 |
91 | By default the Javascript is generated as an array containing objects with the properties (or keys) using the header names and the values as the cell values.
92 |
93 | `As Object` allows you to generate the Javascript as an object with a named property as the array of data e.g.
94 |
95 | ```
96 | {
97 | data: [
98 | {
99 | name: "Monica",
100 | age: "29"
101 | }
102 | ]
103 | }
104 | ```
105 |
106 | ## Property Name
107 |
108 | The `Property Name` text area can be used to change the name of the property representing the data array in the object.
109 |
110 | By default this is `data` but you can amend it to whatever text you require.
111 |
112 | e.g. using `my data` as the `Property Name`
113 |
114 | ```
115 | {
116 | my_data: [
117 | {
118 | name: "Monica",
119 | age: "29"
120 | }
121 | ]
122 | }
123 | ```
124 |
--------------------------------------------------------------------------------
/js/grid/importer.js:
--------------------------------------------------------------------------------
1 | import { DelimiterOptions } from "../data_formats/delimiter-options.js";
2 | import { GherkinConvertor } from "../data_formats/gherkin-convertor.js";
3 | import { MarkdownConvertor, MarkdownOptions } from "../data_formats/markdown-convertor.js";
4 | import { HtmlConvertor, HtmlConvertorOptions } from "../data_formats/html-convertor.js";
5 | import { DelimiterConvertor } from "../data_formats/delimiter-convertor.js";
6 | import { CsvConvertor } from "../data_formats/csv-convertor.js";
7 | import { JsonConvertor, JsonConvertorOptions } from "../data_formats/json-convertor.js";
8 | import { JavascriptConvertor, JavascriptConvertorOptions } from "../data_formats/javascript-convertor.js";
9 | import { fileTypes } from "../data_formats/file-types.js";
10 | import { PapaWrappa } from "../utils/papawrappa.js"
11 |
12 | class Importer{
13 |
14 | constructor(gridExtension) {
15 | this.gridExtensions=gridExtension;
16 |
17 | this.options={};
18 | this.options["csv"] = new DelimiterOptions('"');
19 | this.options["dsv"] = new DelimiterOptions("\t");
20 | // this.options["asciitable"] = new AsciiTableOptions();
21 | this.options["markdown"] = new MarkdownOptions();
22 | this.options["json"] = new JsonConvertorOptions();
23 | this.options["javascript"] = new JavascriptConvertorOptions();
24 | this.options["html"] = new HtmlConvertorOptions();
25 |
26 | this.convertors = {};
27 | this.convertors["markdown"] = new MarkdownConvertor();
28 | this.convertors["gherkin"] = new GherkinConvertor();
29 | this.convertors["html"] = new HtmlConvertor();
30 | this.convertors["dsv"] = new DelimiterConvertor();
31 | this.convertors["dsv"].setPapaParse(new PapaWrappa());
32 | this.convertors["csv"] = new CsvConvertor();
33 | this.convertors["csv"].setPapaParse(new PapaWrappa());
34 | this.convertors["json"] = new JsonConvertor();
35 | this.convertors["javascript"] = new JavascriptConvertor();
36 |
37 | }
38 |
39 | canImport(type){
40 | return this.convertors.hasOwnProperty(type);
41 | }
42 |
43 | getFileExtensionFor(type){
44 | return fileTypes[type]?.fileExtension;
45 | }
46 |
47 | setOptionsForType(type, options){
48 |
49 | if(this.options[type]){
50 | let optionsToUse = this.options[type];
51 | optionsToUse.mergeOptions(options);
52 | }
53 | }
54 |
55 | // text area import
56 | importText(typeToImport, textToImport){
57 |
58 | if(!this.canImport(typeToImport)){
59 | console.log(`Data Type ${typeToImport} not supported for importing`);
60 | return;
61 | }
62 |
63 | let optionsToUse = {};
64 | if(this.options[typeToImport]){
65 | optionsToUse = this.options[typeToImport];
66 | }
67 |
68 | let convertorToUse = this.convertors[typeToImport];
69 | if(convertorToUse !== undefined){
70 | convertorToUse?.setOptions?.(optionsToUse);
71 | this.setGridFromGenericDataTable(convertorToUse.toDataTable(textToImport));
72 | return;
73 | }
74 | }
75 |
76 |
77 | setGridFromGenericDataTable(dataTable){
78 | return this.gridExtensions.setGridFromGenericDataTable(dataTable);
79 | }
80 | }
81 |
82 | export {Importer}
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/json/options.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | title: "Json Options"
4 | description: "Options available for converting to Json in AnyWayData.com. This includes formatting numbers, pretty print control and representing as a named array property."
5 | ---
6 |
7 | The configuration options for JSON are listed below. These are similar to [Javascript](/docs/data-formats/javascript/javascript).
8 |
9 | The main difference between JSON and Javascript is that the key values in JSON will all be represented as String values with `""`.
10 | ## Number Convert
11 |
12 | The `Number Convert` option detects if the 'value' is a number and if so will be output without quotes.
13 |
14 | e.g. with `Number Convert` off
15 |
16 | ```
17 | [
18 | {
19 | "name": "Monica",
20 | "age": "29"
21 | }
22 | ]
23 | ```
24 |
25 | And with `Number Convert` on:
26 |
27 | ```
28 | [
29 | {
30 | "name": "Monica",
31 | "age": 29
32 | }
33 | ]
34 | ```
35 |
36 | ## Pretty Print
37 |
38 | The `Pretty Print` option controls the pretty printing of JSON.
39 |
40 | With no `Pretty Print` the JSON will be minified:
41 |
42 | ```
43 | [{"name":"Monica","age":"29"}]
44 | ```
45 |
46 | With `Pretty Print` enabled the JSON will be generated with the chosen `Delimiter`.
47 |
48 | e.g.
49 |
50 | ```
51 | [
52 | {
53 | "name": "Monica",
54 | "age": "29"
55 | }
56 | ]
57 | ```
58 |
59 | ## Delimiter
60 |
61 | The `Delmiter` is the value used to space out the JSON indentation. This is only used when `Pretty Print` has been set to 'on'.
62 |
63 | The `Delimiter` drop down allows you to choose some standard values:
64 |
65 | - `Tab [\t]` - use tabs to indent the values
66 | - `Space [ ]` - use a single space to indent the values
67 | - `Custom Value` - use the value in the `Custom` Text box.
68 |
69 | ## Custom
70 |
71 | The `Custom` text box allows you to add any indentation string to use during the `Pretty Print` formatting.
72 |
73 | This would normally be used to add spaces e.g. ` ` two spaces or ` ` three spaces.
74 |
75 | It is possible to use this text area with strings like "..." to generate:
76 |
77 | ```
78 | [
79 | ...{
80 | ......"name": "Monica",
81 | ......"age": "29"
82 | ...}
83 | ]
84 | ```
85 |
86 | This would generate invalid JSON but might be useful for some publications or examples.
87 |
88 | When the `Custom` value is a number e.g. `4` then the indentation will use four spaces i.e. ` `
89 |
90 | ## As Object
91 |
92 | By default the JSON is generated as an array containing objects with the properties (or keys) using the header names and the values as the cell values.
93 |
94 | `As Object` allows you to generate the JSON as an object with a named property as the array of data e.g.
95 |
96 | ```
97 | {
98 | "data": [
99 | {
100 | "name": "Monica",
101 | "age": "29"
102 | }
103 | ]
104 | }
105 | ```
106 |
107 | ## Property Name
108 |
109 | The `Property Name` text area can be used to change the name of the property representing the data array in the object.
110 |
111 | By default this is `data` but you can amend it to whatever text you require.
112 |
113 | e.g. using `my data` as the `Property Name`
114 |
115 | ```
116 | {
117 | "my data": [
118 | {
119 | "name": "Monica",
120 | "age": "29"
121 | }
122 | ]
123 | }
124 | ```
--------------------------------------------------------------------------------
/js/data_formats/generic-data-table.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | GenericDataTable will be the intermediate data format for all conversions.
4 |
5 | ie.
6 |
7 | - all import from format will be to a GenericDataTable
8 | - grid setting will be from a GenericDataTable
9 | - grid will export to GenericDataTable
10 | - all exports will format from a GenericData Table
11 |
12 | e.g.
13 |
14 | csv -> to GenericDataTable -> grid
15 | markdown -> to GenericDataTable -> grid
16 | csv -> to GenericDataTable -> markdown
17 | grid -> to GenericDataTable -> markdown
18 |
19 | This way we only have to support conversion to, and from GenericDataTable and this
20 | can be more easily unit tested.
21 |
22 | */
23 | class GenericDataTable {
24 |
25 | constructor() {
26 | this.headers = [];
27 | this.rows = [];
28 | }
29 |
30 | clear(){
31 | // clear the table
32 | this.headers = [];
33 | this.rows=[];
34 | return true;
35 | }
36 |
37 | appendDataRow(rowData){
38 | let numberOfEmptyCellsToAdd = this.headers.length;
39 | let rowToAdd = [];
40 | if(rowData!==undefined && rowData !==null){
41 | if(rowData.length>this.headers.length){
42 | // problem, the row is too long for the table
43 | console.log("Tried to add " + rowData.length + " columns to a table with " + this.headers.length + " columns");
44 | return false;
45 | }
46 | // add the data into the row
47 | numberOfEmptyCellsToAdd = this.headers.length - rowData.length;
48 |
49 | for(let cell of rowData){
50 | rowToAdd.push(cell ? String(cell) : '');
51 | }
52 | }
53 |
54 | // add the empty cells
55 | while(numberOfEmptyCellsToAdd>0){
56 | rowToAdd.push("");
57 | numberOfEmptyCellsToAdd--;
58 | }
59 |
60 | this.rows.push(rowData);
61 | return true;
62 | }
63 |
64 | addHeader(aHeader){
65 | this.headers.push(aHeader ? String(aHeader) : '');
66 | }
67 |
68 | setHeaders(aHeaderArray){
69 | this.headers = [];
70 | for(let header of aHeaderArray){
71 | this.addHeader(header);
72 | }
73 | }
74 |
75 | getColumnCount(){
76 | return this.headers.length;
77 | }
78 |
79 | getHeader(headerIndex){
80 | return this.headers[headerIndex];
81 | }
82 |
83 | getHeaders(){
84 | return this.headers.map(header => header);
85 | }
86 |
87 | getRowCount(){
88 | return this.rows.length;
89 | }
90 |
91 | getCell(rowIndex, colIndex){
92 | return this.rows[rowIndex][colIndex];
93 | }
94 |
95 | getRow(rowIndex){
96 | return this.rows[rowIndex].map(cell => cell);
97 | }
98 |
99 | getRowAsObject(rowIndex){
100 | let fieldnames = this.getHeaders();
101 | return this.getRowAsObjectUsingHeadings(rowIndex, fieldnames);
102 | }
103 |
104 | getRowAsObjectUsingHeadings(rowIndex, fieldnames){
105 | let vals = {};
106 | let row = this.getRow(rowIndex);
107 | for (const propertyid in fieldnames) {
108 | vals[fieldnames[propertyid]] = row[propertyid];
109 | }
110 | return vals;
111 | }
112 | }
113 |
114 | export {GenericDataTable}
--------------------------------------------------------------------------------
/js/gui_components/tabbed-text-control.js:
--------------------------------------------------------------------------------
1 | class TabbedTextControl{
2 |
3 | constructor(parentElement, theImportExportControls) {
4 | this.parent = parentElement;
5 | this.importExportController = theImportExportControls;
6 | }
7 |
8 | // TODO : populate this from the registered importers and exporters rather than hard coding
9 | addToGui(){
10 | this.parent.innerHTML =
11 | `
12 |
66 | `;
67 | }
68 |
69 | setApplyCallback(callbackFunc){
70 |
71 | let button = this.parent.querySelector(".delimited-options .apply button");
72 | button.onclick = function (){
73 | callbackFunc(this.getOptionsFromGui())
74 | }.bind(this);
75 |
76 | }
77 |
78 | getOptionsFromGui(){
79 |
80 | // TODO : create a CsvDelimiterOptions()
81 | let delimiterOptions = new DelimiterOptions(",");
82 |
83 | delimiterOptions.options.quotes = this.htmlData.getCheckBoxValueFrom(".quotes label input");
84 | delimiterOptions.options.header = this.htmlData.getCheckBoxValueFrom(".headerval label input");
85 | delimiterOptions.options.quoteChar = this.htmlData.getTextInputValueFrom(".quoteChar label input");
86 | delimiterOptions.options.escapeChar = this.htmlData.getTextInputValueFrom(".escapeChar label input");
87 | return delimiterOptions;
88 | }
89 |
90 | setFromOptions(delimiterOptions){
91 |
92 | if(!delimiterOptions.options){
93 | return;
94 | }
95 |
96 | let options = delimiterOptions.options;
97 |
98 | this.htmlData.setCheckBoxFrom(".quotes label input", options.quotes, true);
99 | this.htmlData.setCheckBoxFrom(".headerval label input", options.header, true);
100 | this.htmlData.setTextFieldToValue(".quoteChar label input", options.quoteChar, "\"");
101 | this.htmlData.setTextFieldToValue(".escapeChar label input", options.escapeChar, "\"");
102 | }
103 |
104 | }
105 |
106 | export {CsvDelimitedOptions};
--------------------------------------------------------------------------------
/js/gui_components/options_panels/html-options-data-utils.js:
--------------------------------------------------------------------------------
1 | class HtmlDataValues{
2 |
3 | constructor(parentElement) {
4 | this.parent = parentElement;
5 | }
6 |
7 | getSelectedValueFrom(selector, adefault){
8 | let selectElem = this.parent.querySelector(selector);
9 | let value=undefined;
10 | if(selectElem){
11 | value= selectElem.options[selectElem.selectedIndex].value;
12 | }
13 | return value ? value : adefault;
14 | }
15 |
16 | getCheckBoxValueFrom(locator){
17 | let checkboxElem =this.parent.querySelector(locator);
18 | return checkboxElem.checked;
19 | }
20 |
21 | setCheckBoxFrom(locator,value, adefault){
22 | let elem = this.parent.querySelector(locator);
23 | let valueToUse = value!==undefined ? value : adefault;
24 |
25 | if(elem){
26 | elem.checked = valueToUse;
27 | }
28 | }
29 |
30 | setDropDownOptionToKeyValue(locator, key, adefault){
31 | let elem = this.parent.querySelector(`${locator} option[value='${key}']`);
32 | if(elem){
33 | elem.selected=true;
34 | }else{
35 | elem = this.parent.querySelector(`${locator} option[value='${adefault}']`);
36 | if(elem){
37 | elem.selected=true;
38 | }
39 | }
40 | }
41 |
42 | setTextFieldToValue(locator, value){
43 | let setValue = value!==undefined ? value : "";
44 |
45 | let elem = this.parent.querySelector(locator);
46 | if(elem){
47 | elem.value=setValue;
48 | }
49 | }
50 |
51 | getTextInputValueFrom(locator){
52 | let elem = this.parent.querySelector(locator);
53 | if(elem){
54 | return elem.value;
55 | }else{
56 | return "";
57 | }
58 | }
59 |
60 | getSelectWithCustomInput(selectLocator, customItemKey, inputLocator, keyMappings, aDefault){
61 |
62 | let selectElem = this.parent.querySelector(selectLocator);
63 | let delimiterName = selectElem.options[selectElem.selectedIndex].value;
64 | let delimiter=undefined;
65 | if(delimiterName===customItemKey){
66 | delimiter = this.parent.querySelector(inputLocator).value;
67 | }else{
68 | for(const key in keyMappings){
69 | if(delimiterName===key){
70 | delimiter = keyMappings[key];
71 | }
72 | }
73 | }
74 |
75 | if(delimiter===undefined){
76 | console.log("unknown item found - using default");
77 | delimiter=aDefault;
78 | }
79 |
80 | return delimiter;
81 | }
82 |
83 | setSelectWithCustomInput(selectLocator, customItemKey, inputLocator, keyMappings, theValue){
84 | // set input to empty
85 | this.parent.querySelector(inputLocator).value="";
86 |
87 | // vind the value in the key mappings if present
88 | let foundDelim=false;
89 | for(const key in keyMappings){
90 | if(theValue===keyMappings[key]){
91 | let optionelem = this.parent.querySelector(selectLocator + ` option[value='${key}']`);
92 | if(optionelem!==undefined){
93 | optionelem.selected=true;
94 | }
95 |
96 | foundDelim=true;
97 | }
98 | }
99 |
100 | // if not there then set to the custom
101 | if(!foundDelim){
102 | let optionelem = this.parent.querySelector(selectLocator + ` option[value='${customItemKey}']`);
103 | if(optionelem!==undefined){
104 | optionelem.selected=true;
105 | }
106 | // and set the text field
107 | this.parent.querySelector(inputLocator).value = theValue;
108 | }
109 | }
110 |
111 | }
112 |
113 |
114 | export {HtmlDataValues}
--------------------------------------------------------------------------------
/js/grid/exporter.js:
--------------------------------------------------------------------------------
1 | import { GherkinConvertor, GherkinOptions } from "../data_formats/gherkin-convertor.js";
2 | import { MarkdownConvertor, MarkdownOptions } from "../data_formats/markdown-convertor.js";
3 | import { HtmlConvertor, HtmlConvertorOptions } from "../data_formats/html-convertor.js";
4 | import { JsonConvertor, JsonConvertorOptions } from "../data_formats/json-convertor.js";
5 | import { JavascriptConvertor, JavascriptConvertorOptions } from "../data_formats/javascript-convertor.js";
6 | import { CsvConvertor } from "../data_formats/csv-convertor.js";
7 | import { DelimiterConvertor } from "../data_formats/delimiter-convertor.js";
8 | import { DelimiterOptions } from "../data_formats/delimiter-options.js";
9 | import { AsciiTableConvertor, AsciiTableOptions } from "../data_formats/asciitable-convertor.js";
10 | import { fileTypes } from '../data_formats/file-types.js';
11 | import { PapaWrappa } from "../utils/papawrappa.js";
12 |
13 | class Exporter {
14 |
15 | constructor(gridExtensions) {
16 | // TODO: this should be a higher level api than the low level ag-grid api
17 | // e.g. move to grid extensions or a GridExportExtensions
18 | this.gridExtensions = gridExtensions;
19 |
20 | this.options={};
21 | this.options["csv"] = new DelimiterOptions('"');
22 | this.options["dsv"] = new DelimiterOptions("\t");
23 | this.options["asciitable"] = new AsciiTableOptions();
24 | this.options["markdown"] = new MarkdownOptions();
25 | this.options["json"] = new JsonConvertorOptions();
26 | this.options["javascript"] = new JavascriptConvertorOptions();
27 | this.options["html"] = new HtmlConvertorOptions();
28 | this.options["gherkin"] = new GherkinOptions();
29 |
30 | this.exporters = {};
31 | this.exporters["markdown"]= new MarkdownConvertor();
32 | this.exporters["csv"]= new CsvConvertor();
33 | this.exporters["csv"].setPapaParse(new PapaWrappa())
34 | this.exporters["dsv"]= new DelimiterConvertor();
35 | this.exporters["dsv"].setPapaParse(new PapaWrappa())
36 | this.exporters["json"] = new JsonConvertor();
37 | this.exporters["javascript"] = new JavascriptConvertor();
38 | this.exporters["gherkin"] = new GherkinConvertor();
39 | this.exporters["html"] = new HtmlConvertor();
40 | this.exporters["asciitable"] = new AsciiTableConvertor();
41 | }
42 |
43 | canExport(type){
44 | return this.exporters.hasOwnProperty(type);
45 | }
46 |
47 | getFileExtensionFor(type){
48 | return fileTypes[type].fileExtension;
49 | }
50 |
51 | getGridAs(type){
52 |
53 | if(!this.canExport(type)){
54 | console.log(`Data Type ${type} not supported for exporting`);
55 | return "";
56 | }
57 |
58 | if(this.exporters.hasOwnProperty(type)){
59 | let exporterToUse = this.exporters[type];
60 | let optionsToUse = this.options[type];
61 | if(optionsToUse!==undefined){
62 | exporterToUse?.setOptions?.(optionsToUse);
63 | }
64 | return exporterToUse?.fromDataTable(this.getGridAsGenericDataTable());
65 | }
66 | }
67 |
68 | getGridAsGenericDataTable(){
69 | return this.gridExtensions.getGridAsGenericDataTable();
70 | }
71 |
72 | getHeadersFromGrid(){
73 | return this.gridExtensions.getHeadersFromGrid();
74 | }
75 |
76 |
77 | getOptionsForType(type){
78 | return this.options[type];
79 | }
80 |
81 | setOptionsForType(type, options){
82 | if(this.options[type]){
83 | let optionsToUse = this.options[type];
84 | optionsToUse.mergeOptions(options);
85 |
86 | // because we can switch headers off for these types
87 | // we need to remember them
88 | if(type==="csv" || type ==="dsv"){
89 |
90 | if(optionsToUse.header===false){
91 | // store headers from the grid in the options
92 | optionsToUse.headers = this.getHeadersFromGrid();
93 | }
94 | }
95 | }
96 | }
97 | }
98 |
99 | export {Exporter}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/eviltester/grid-table-editor/actions/workflows/node.js.yml)
2 |
3 | [](https://wallabyjs.com/oss/)
4 |
5 | # Data Grid Table Editor and Data Generator
6 |
7 | A simple Data Table Editor that generates Markdown, CSV or JSON. It can also be used to interchange data between the formats, supporting editing in the grid.
8 |
9 | - Grid interface
10 | - drag and drop rows to re-order
11 | - drag and drop columns to re-order
12 | - import csv from file
13 | - import csv, markdown, and json from editor
14 | - generate csv, markdown, json and javascript from grid
15 | - export csv, markdown and json files
16 | - add and delete columns
17 | - add and delete rows
18 | - randomly fill data with Test Data
19 | - configure export options
20 |
21 | ---
22 |
23 | ## Use The Editor
24 |
25 | The application is live at [AnyWayData.com](https://anywaydata.com)
26 |
27 | - Or clone and run locally by opening index.html in a browser after starting a web server in the folder e.g. `python3 -m http.server`
28 |
29 | ## To Generate Test Data
30 |
31 | Expand the "> Test Data" section.
32 |
33 | Enter a spec.
34 |
35 | The spec is a paragraph of text where each line is either a 'name' or a 'rule':
36 |
37 | ```
38 | name
39 | rule
40 | name
41 | rule
42 | ```
43 |
44 | - `name` will be used as a column name
45 | - `rule` will be used to generate the data
46 |
47 | A `rule` can be a regex string e.g.
48 |
49 | - `(connie|bob)` which would generate 'connie' or 'bob'
50 | - `[1-9][0-9]{0,4}` which would generate number between 1 and 99999
51 |
52 | A `rule` can also be a faker API call.
53 |
54 | Faker API can be found here: https://fakerjs.dev/api/
55 |
56 | e.g.
57 |
58 | - `faker.person.firstName`
59 | - `faker.hacker.noun`
60 |
61 | The `faker` prefix is optional:
62 |
63 | e.g.
64 |
65 | - `person.firstName`
66 | - `hacker.noun`
67 |
68 | The `fake` method is also supported, which takes a mustache template style string combining api methods e.g.
69 |
70 | - `helpers.fake("{{name.lastName}}, {{name.firstName}}")`
71 |
72 | So a sample test data spec might look like:
73 |
74 | ```
75 | name
76 | helpers.fake("{{name.lastName}}, {{name.firstName}}")
77 | desc
78 | faker.lorem.paragraph
79 | collects
80 | hacker.noun
81 | prefers
82 | (Connie|Bob)
83 | ```
84 |
85 | ## Similar Apps
86 |
87 | Looking for similar apps to compare features sets and functionality?
88 |
89 | There is a maintained list in the documentation:
90 |
91 | - [Related Tools](https://anywaydata.com/docs/misc/related_tools)
92 |
93 |
94 | ## References
95 |
96 | - [Markdown Tables Extended Syntax](https://www.markdownguide.org/extended-syntax/#tables)
97 |
98 | ## Libraries Used
99 |
100 | - [Faker.js](http://marak.github.io/faker.js) for domain api
101 | - http://marak.github.io/faker.js
102 | - [RandExp.js](http://fent.github.io/randexp.js/) for regular expression based random data generation
103 | - http://fent.github.io/randexp.js/
104 | - [AG Grid](https://ag-grid.com) for the data table
105 | - [PapaParse](https://www.papaparse.com/) for csv processing
106 |
107 | ## Tool Categories
108 |
109 | This tool falls in to the categories:
110 |
111 | - online markdown editor
112 | - markdown table generator
113 | - markdown table editor
114 | - Online Test Data Generation
115 | - Online CSV Editor
116 |
117 |
118 | ## Building
119 |
120 | Test - `npm test`
121 |
122 | Preview Docs - `npm run previewdocs`
123 |
124 | Build for release - `npm run anywaydata:win`
125 |
126 | ## Probable TODO:
127 |
128 | - TODO: add a preview toggle for the 'text editor' to only show a few rows - should also disable editor to grid buttons
129 | - TODO: convert all JS to TypeScript
130 | - https://blog.logrocket.com/a-simple-guide-for-migrating-from-javascript-to-typescript/
131 | - TODO: improve and expand the test data generation - written in TypeScript, don't use faker directly - create a full semantic abstraction
132 | - TODO: create a DSL for the test data generation... probably just JSON initially
133 | - TODO: convert UI to use React?
--------------------------------------------------------------------------------
/js/data_formats/json-convertor.js:
--------------------------------------------------------------------------------
1 | import { isNumber } from "../utils/number-convertor.js";
2 | import { GenericDataTableUtils } from "./generic-data-table-utils.js";
3 | import { GenericDataTable } from "./generic-data-table.js";
4 |
5 | class JsonConvertorOptions{
6 |
7 | constructor(){
8 |
9 | this.delimiterMappings={
10 | "tab": "\t",
11 | "dot": ".",
12 | "dash": "-",
13 | "underline": "_",
14 | "space": " ",
15 | "plus": "+",
16 | }
17 |
18 | this.options = {
19 | // make numbers numeric - by default everything is a string by support numbers being unquoted
20 | makeNumbersNumeric: false,
21 | //pretty print or not - when not then every row is shown on same line and minified
22 | prettyPrint: true,
23 | // pretty print delimiter or this could be a number of spaces
24 | prettyPrintDelimiter: `\t`,
25 | // output as array (false), or object with property named below
26 | asObject:false,
27 | // as object with user configurable key name e.g. {"table":[...jsonrows]}
28 | asPropertyNamed: "data"
29 | };
30 |
31 | this.headerNameConvertor = (x)=>x;
32 | }
33 |
34 | mergeOptions(newoptions){
35 | if(newoptions.options){
36 | this.options = {...this.options, ...newoptions.options}
37 | }else{
38 | this.options = {...this.options, ...newoptions}
39 | }
40 |
41 | if(newoptions.headerNameConvertor){
42 | this.headerNameConvertor = newoptions.headerNameConvertor;
43 | }
44 | }
45 | }
46 |
47 | class JsonConvertor {
48 |
49 | constructor(params) {
50 |
51 | this.config = new JsonConvertorOptions();
52 |
53 | if(params!==undefined){
54 | this.setOptions(params);
55 | }
56 | }
57 |
58 | setOptions(newOptions){
59 | this.config.mergeOptions(newOptions);
60 | }
61 |
62 | formatAsObjects(dataTable){
63 |
64 | let fieldnames = dataTable.getHeaders().map(
65 | header => this.config.headerNameConvertor(header)
66 | );
67 | let objects = [];
68 |
69 | for(let rowIndex=0; rowIndex {
82 | if((value*1)==0){return 0;} // special case 0
83 | return (value == value * 1 ) ? value * 1 : value;
84 | }
85 | }
86 |
87 | let delimiter = null;
88 | if(this.config.options.prettyPrint){
89 | delimiter = this.config.options.prettyPrintDelimiter;
90 | }
91 | if(isNumber(delimiter)){
92 | delimiter = delimiter*1;
93 | }
94 |
95 | let data = this.formatAsObjects(dataTable);
96 |
97 | let toOutput = undefined;
98 | if(this.config.options.asObject){
99 | toOutput = {}
100 | toOutput[this.config.options.asPropertyNamed]=data;
101 | }else{
102 | toOutput = data;
103 | }
104 | return JSON.stringify(toOutput, replacer, delimiter);
105 | }
106 |
107 |
108 | toDataTable(textToImport){
109 |
110 | let jsonArray = [];
111 | if(this.config.options.asObject){
112 | let parsedJson = JSON.parse(textToImport);
113 | jsonArray = parsedJson[this.config.options.asPropertyNamed];
114 | }else{
115 | jsonArray = JSON.parse(textToImport);
116 | }
117 |
118 | const utils = new GenericDataTableUtils();
119 | let dataTable = new GenericDataTable();
120 | utils.setGenericDataTableFromDataObjects(dataTable, jsonArray);
121 |
122 | return dataTable;
123 | }
124 |
125 | }
126 |
127 | export {JsonConvertor, JsonConvertorOptions}
--------------------------------------------------------------------------------
/docs-src/docs/030-data-formats/html/010-html-tables.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: "HTML Tables"
4 | description: "HTML Tables are a default way to represent tabular data on web pages, they offer no interactivity but can be styled via CSS to be more user friendly."
5 | ---
6 |
7 | HTML Tables are a default way to represent tabular data on web pages, they offer no interactivity but can be styled via CSS to be more user friendly.
8 |
9 | ## What is an HTML Table
10 |
11 | HTML tables are the default way to represent tabular data on web pages. They have a few minimal elements to react a table:
12 |
13 | - parent `table` element
14 | - each row in the table is a `tr` element
15 | - column headers are child `th` elements
16 | - each column in a data row is a `td` element
17 |
18 | e.g.
19 |
20 | ```
21 |
22 |
23 |
Country
24 |
Population
25 |
World Share
26 |
27 |
28 |
China
29 |
1,439,323,776
30 |
18.47 %
31 |
32 |
33 |
India
34 |
1,380,004,385
35 |
17.70 %
36 |
37 |
38 |
United States
39 |
331,002,651
40 |
4.25 %
41 |
42 |
43 | ```
44 |
45 | Would render as:
46 |
47 |
48 |
49 |
Country
50 |
Population
51 |
World Share
52 |
53 |
54 |
China
55 |
1,439,323,776
56 |
18.47 %
57 |
58 |
59 |
India
60 |
1,380,004,385
61 |
17.70 %
62 |
63 |
64 |
United States
65 |
331,002,651
66 |
4.25 %
67 |
68 |
69 |
70 | Note that different browsers render HTML tables without any styling differently, some do not add borders, or embolden headers etc. Styling should be added to make HTML tables readable.
71 |
72 | ## thead and tbody
73 |
74 | Additionally `thead` and `tbody` group elements can be used to represent the header and body section.
75 |
76 | ```
77 |
78 |
79 |
80 |
Country
81 |
Population
82 |
World Share
83 |
84 |
85 |
86 |
87 |
China
88 |
1,439,323,776
89 |
18.47 %
90 |
91 |
92 |
India
93 |
1,380,004,385
94 |
17.70 %
95 |
96 |
97 |
United States
98 |
331,002,651
99 |
4.25 %
100 |
101 |
102 |
103 | ```
104 |
105 | This can provide additional opportunities for styling via CSS and can make the HTML code easier to read.
106 |
107 |
108 |
109 |
110 |
Country
111 |
Population
112 |
World Share
113 |
114 |
115 |
116 |
117 |
China
118 |
1,439,323,776
119 |
18.47 %
120 |
121 |
122 |
India
123 |
1,380,004,385
124 |
17.70 %
125 |
126 |
127 |
United States
128 |
331,002,651
129 |
4.25 %
130 |
131 |
132 |
133 |
134 | ## Support for Delimited Files
135 |
136 | AnyWayData.com allows both import and export of HTML.
137 |
138 | The import of HTML tables is particularly useful when testing because you may want to use data published on a web site
139 |
140 | The HTML code representing the table can be pasted into AnyWayData.com and imported into the data grid to be converted into other formats or edited.
141 |
142 | Styles and HTML controls are removed during the import process so the data grid will contain the raw data without any styling.
143 |
144 | AnyWayData does not support cell spanning or row spanning e.g. `colspan` or `rowspan` attributes. Tables will still be imported but you may see some 'undefined' entries as values.
145 |
146 |
147 |
--------------------------------------------------------------------------------
/cli/readme.md:
--------------------------------------------------------------------------------
1 | ## Simple CLI Test Data Generator
2 |
3 | Experiment with bun to create a CLI file using existing sources as much as possible.
4 |
5 | Re-using code from AnyWayData.com web app. Generate Test Data using the same file specification.
6 |
7 | Faker API is documented here:
8 |
9 | - https://fakerjs.dev/api/
10 |
11 | AnyWayData Test Generation is described here:
12 |
13 | - https://anywaydata.com/docs/test-data/test-data-generation
14 | - https://anywaydata.com/docs/test-data/regex-test-data
15 | - https://anywaydata.com/docs/test-data/faker-test-data
16 |
17 | ## Example usage
18 |
19 | Given a file of test data spec as input in `exampleTestDataSpec.txt` generate 3 lines of output to console.
20 |
21 | `anywaydata generate -i exampleTestDataSpec.txt -n 3`
22 |
23 | Output file can be specified with `-o` option.
24 |
25 | `anywaydata generate -i exampleTestDataSpec.txt -n 3 -o output.txt`
26 |
27 | Outputting to a file using standard out redirection.
28 |
29 | `anywaydata generate -i exampleTestDataSpec.txt -n 3 > output.csv`
30 |
31 |
32 | ## Test Data Specification File Format
33 |
34 | The file format is a very simple.
35 |
36 | - Each Column of data is specified as the column name on one line, and the data spec on next line
37 | - Multiple Columns can be created by adding multiple two line sequences
38 |
39 | e.g.
40 |
41 | ```
42 | Column Name 1
43 | Data Value Spec for Column Name 1
44 | Column Name 2
45 | Data Value Spec for Column Name 2
46 | ```
47 |
48 | ### Data Value Specifications - Literal Values
49 |
50 | e.g.
51 |
52 | ```
53 | Company
54 | AnyWayData
55 | ```
56 | Above creates a single field called `Company` with the String literal value `AnyWayData`
57 |
58 | Literal data would be repeated e.g.
59 |
60 | ```
61 | anywaydata generate -i ./examples/company-literal.txt -n 3
62 | "Company"
63 | "AnyWayData"
64 | "AnyWayData"
65 | "AnyWayData"
66 | ```
67 |
68 | ### Data Value Specifications - Faker Random Data Values
69 |
70 | Faker Data can be used as the data spec e.g.
71 |
72 | ```
73 | Company
74 | company.name
75 | ```
76 |
77 | This could be used as follows:
78 |
79 | ```
80 | > anywaydata generate -i ./examples/company.txt -n 3
81 | "Company"
82 | "Lowe LLC"
83 | "Stroman, Hills and Bauch"
84 | "Torp - Gutkowski"
85 | ```
86 |
87 | The faker API is documented here: https://fakerjs.dev/api/
88 |
89 | ### Data Value Specifications - Regex Random Data Values
90 |
91 | Any data spec value that is not identified as a faker value is treated as a regex. Literals are actually processed as a regex.
92 |
93 | So if you wanted a literal with a "." you would have to escape it.
94 |
95 | ```
96 | Company
97 | AnyWayData\.com
98 | ```
99 |
100 | But this means we can generate data that Faker does not support. e.g. custom fields with 3 to 6 letters A-Z followed by 0 to 6 digits
101 |
102 | ```
103 | Regex Generated Field
104 | [A-Z]{3,6}[0-9]{0,6}
105 | ```
106 |
107 | Would generate something like:
108 |
109 | ```
110 | > anywaydata generate -i ./examples/regex-field.txt -n 3
111 | "Regex Generated Field"
112 | "OFQ6"
113 | "OPXSE"
114 | "LVQF5162"
115 | ```
116 |
117 | ### Dev Notes
118 |
119 | This is a prototype using Bun
120 |
121 | https://bun.sh/
122 |
123 | To dev and test
124 |
125 | ```
126 | bun link anywaydata
127 | ```
128 |
129 | Then edit and test.
130 |
131 |
132 | ### Building Executables
133 |
134 | ```
135 | bun build ./index.ts --outdir ./out --target bun
136 | ```
137 |
138 | Creating executables:
139 |
140 | https://bun.sh/docs/bundler/executables
141 |
142 |
143 | ```
144 | bun build ./index.ts --compile --minify --sourcemap --target=bun-windows-x64 --outfile ./out/windows64/anywaydata.exe
145 | ```
146 |
147 |
148 | // ENOENT: Failed to move cross-compiled bun binary into cache directory C:\Users\user\.bun\install\cache\bun-linux-x64-v1.2.10
149 | // need to build on the c:\ drive
150 | ```
151 | bun build ./index.ts --compile --outfile ./out/linux-x64/anywaydata --target=bun-linux-x64
152 | ```
153 |
154 | ```
155 | bun build ./index.ts --compile --outfile ./out/linux-arm64/anywaydata --target=bun-linux-arm64
156 | ```
157 |
158 | ```
159 | bun build ./index.ts --compile --outfile ./out/macos-x64/anywaydata --target=bun-darwin-x64
160 | ```
161 |
162 | ```
163 | bun build ./index.ts --compile --outfile ./out/macos-arm64/anywaydata --target=bun-darwin-arm64
164 | ```
165 |
166 | ## TODO
167 |
168 | - Add tests around the CLI code
169 | - Auto generate executables from within Github
--------------------------------------------------------------------------------
/js/gui_components/data-grid-editor/gridControl.js:
--------------------------------------------------------------------------------
1 | class GridControlsPageMap{
2 |
3 | constructor(){
4 | this.addRowButtonQuery = "#addRowButton";
5 | this.addRowsAboveButtonQuery = "#addRowsAboveButton";
6 | this.addRowsBelowButtonQuery = "#addRowsBelowButton";
7 | this.deleteSelectedRowsButtonQuery = "#deleteSelectedRowsButton";
8 | this.clearFiltersButtonQuery = "#clearFiltersButton";
9 | this.filtersTextBoxQuery = "#filter-text-box";
10 | this.clearTableButtonQuery ="#clearTableButton";
11 | }
12 | }
13 |
14 | // TODO : don't hook into existing controls in HTML create them here and then hook into them
15 | // The buttons above a grid
16 | class GridControl {
17 |
18 | constructor(pageMap) {
19 |
20 | this.pageMap = pageMap;
21 | }
22 |
23 | // TODO : avoid hard coded IDs use relative to the parent, so store the parent e.g. like option panels
24 | addGuiIn(parent){
25 | parent.innerHTML =`
26 |