Skip to content

Commit

Permalink
Creates a graphql-cli plugin package called validate-magento-pwa-queries
Browse files Browse the repository at this point in the history
Creates a compatibility definition file and script to generate markdown from it

Creates a graphql-cli extension to validate a project's queries

Created a new project / repository called graphql-cli-validate-queries that - based on configuration - goes through all of a project's
GraphQL queries and runs them against an endpoint's schema.

Updates venia-concept to use this new extension and the graphql-cli tool in general.

Resolves #952.

Fixes the format of the graphql/template-strings eslint rule

Adds script to dev-docs to auto-generate the magento compatibility table

Adds a README to the graphql-cli-validate-queries  project

Adds unit tests to graphql-cli-validate-queries plugin.

Installs and wires up Jest for unit testing the validate-queries plugin.

Fixed typo mistakes. (#993)

* Fixed typo mistakes

* Fixed typo mistakes

chore: remove duplicate padding property (#941)

[BUGFIX] [ISSUE-958] Autocomplete renders loading component on clear (#961)

* [BUGFIX] [ISSUE-958] Autocomplete renders loading component on clear

Removes invalid TODO in graphql-cli-validate-queries/index.js

Adds a JSDoc comment block to the getValidator function in graphql-cli-validate-queries

Cleans up the devdoc draft for validate-queries plugin

Adds a PWA-specific summarization to the end of the validate-queries command.

Adds a new script summarize-validate-queries.js to venia-concept
Runs this script at the end of the validate-queries script
This script uses the compatibility table to clue developers to compatibility issues and how to resolve them

Runs prettier

Removes use of reserved word in summarize-validate-queries.js

Create compatibility table page (#1016)

* Fix auto-generated table markdown and re-order versions to list the latest version first

* Incorporate build-compatibility table script into build process

* Create a page to display the compatibility table

* Update pwa-devdocs/src/technologies/magento-compatibility/index.md

Co-Authored-By: jcalcaben <[email protected]>

Updates the path to the compatibility table on the docs site

Updates the wording around the reporting of version specifics and compatibility in the summarize-validate-queries script

Runs prettier and updates summarize-validate-queries.

Fixes dependencies and includes plugin in root jest test.

The graphql-cli-validate-magento-pwa-queries plugins tests are now run as part of the root test command.
This ensures that breaking tests in this project will fail the prepush check.

Cleans up yarn.lock changes by selectively installing dependencies via yarn workspace add commands.

Renames plugin to be more magento and pwa specific.

Runs prettier

Removes unneeded summary script file

Updates the dev docs for the graphql-cli plugin
  • Loading branch information
zetlen authored and supernova-at committed Mar 13, 2019
1 parent 94cdc53 commit a156a48
Show file tree
Hide file tree
Showing 19 changed files with 2,046 additions and 583 deletions.
10 changes: 9 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,15 @@ const jestConfig = {
configureProject('scripts', 'CI Scripts', () => ({
testEnvironment: 'node',
testMatch: [`<rootDir>/scripts/${testGlob}`]
}))
})),
// Test the graphql-cli plugin
configureProject(
'graphql-cli-validate-magento-pwa-queries',
'GraphQL CLI Plugin',
() => ({
testEnvironment: 'node'
})
)
],
// Include files with zero tests in overall coverage analysis by specifying
// coverage paths manually.
Expand Down
9 changes: 9 additions & 0 deletions magento-compatibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* This file describes PWA Studio to Magento version compatabilities.
*/

// PWA Studio version -> Magento version.
module.exports = {
'>2.0.0': '2.3.1',
'2.0.0': '2.3.0'
};
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "2.1.0-dev",
"private": true,
"workspaces": [
"packages/graphql-cli-validate-magento-pwa-queries",
"packages/peregrine",
"packages/pwa-buildpack",
"packages/venia-concept",
Expand Down Expand Up @@ -59,8 +60,6 @@
"eslint-plugin-react": "^7.12.4",
"execa": "~1.0.0",
"figures": "~2.0.0",
"graphql": "^14.1.1",
"graphql-tag": "^2.10.1",
"husky": "~1.3.1",
"jest": "^24.3.1",
"jest-fetch-mock": "^2.1.1",
Expand Down
21 changes: 21 additions & 0 deletions packages/graphql-cli-validate-magento-pwa-queries/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 Adobe Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
65 changes: 65 additions & 0 deletions packages/graphql-cli-validate-magento-pwa-queries/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
[![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest) [![jest](https://jestjs.io/img/jest-badge.svg)](https://github.com/facebook/jest)


# graphql-cli-validate-queries

Validate your project's GraphQL queries against a schema.

## Installation

```
yarn add graphql-cli graphql-cli-validate-queries
```

## Summary

Given the following `.graphqlconfig`:

```
{
"projects": {
"myApp": {
"schemaPath": "mySchema.json",
"extensions": {
"endpoints": {
"default": "https://myEndpoint.com/graphql"
}
}
}
}
}
```

The command
```
graphql-cli get-schema --project myApp
```
will [download the GraphQL schema](https://oss.prisma.io/content/graphql-cli/06-schema-handling)
from `https://myEndpoint.com/graphql` and store it in `mySchema.json`.

Then the command
```
graphql-cli validate-queries --project myApp --filesGlob \"src/**/*.{js,graphql,gql}\" --clients apollo literal
```

will validate all `apollo` and `literal` GraphQL queries it finds in `.js`, `.graphql`, or `.gql` files in the `src/` directory
against that schema.

## Options

This plugin supports the following command line options:

| Option | Description | Type | Default |
| --- | --- | --- | --- |
| `--clients`, `-c` | GraphQL clients in use in this project. | `array` | `["apollo"]` |
| `--filesGlob`, `-f` | A glob used to target files for validation. | `string` | `""` |
| `--project`, `-p` | The project name as specified in `.graphqlconfig`. | `string` | `""` |

## Further Reading

* [graphql-config](https://github.com/prisma/graphql-config)
* [graphql-cli](https://github.com/graphql-cli/graphql-cli)
* [eslint-plugin-graphql](https://github.com/apollographql/eslint-plugin-graphql)
* [graphql/no-deprecated-fields rule](https://github.com/apollographql/eslint-plugin-graphql#no-deprecated-fields-validation-rule)


3 changes: 3 additions & 0 deletions packages/graphql-cli-validate-magento-pwa-queries/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const plugin = require('./lib/index.js');

module.exports = plugin;
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
const plugin = require('../index');

const fs = require('fs');
const eslint = require('eslint');

jest.mock('fs');
jest.mock('eslint');

test('it exports the correct command name', () => {
expect(plugin.command).toBe('validate-magento-pwa-queries');
});

test('it exports a description', () => {
expect(plugin.desc).toBeTruthy();
});

describe('supportedArguments', () => {
test('it supports a project command line argument', () => {
const keys = Object.keys(plugin.supportedArguments);

expect(keys).toHaveLength(1);
expect(keys).toContain('project');
});
});

describe('builder', () => {
const mockArgs = {
options: jest.fn()
};
afterEach(() => {
mockArgs.options.mockClear();
});

test('it is a function', () => {
expect(plugin.builder).toBeInstanceOf(Function);
});

test('it calls args.options with the correct supported arguments', () => {
plugin.builder(mockArgs);

expect(mockArgs.options).toHaveBeenCalled();
expect(mockArgs.options).toHaveBeenCalledWith(
plugin.supportedArguments
);
});
});

describe('handler', () => {
const mockArgs = {
project: 'myApp'
};
const mockContext = {
getProjectConfig: jest.fn(() => {
return Promise.resolve({
config: {
extensions: {
'validate-magento-pwa-queries': {
clients: ['apollo', 'literal'],
filesGlob: '*.graphql'
}
},
schemaPath: 'unit test'
}
});
}),
spinner: {
fail: jest.fn(),
start: jest.fn(),
succeed: jest.fn()
}
};

let eslintCLIEngineSpy;
let existsSyncSpy;
let mockConsoleLog;
let mockConsoleWarn;
let mockProcessExit;

beforeAll(() => {
const noop = () => {};

// For happy paths, mock a report that indicates no errors.
eslintCLIEngineSpy = jest.spyOn(eslint, 'CLIEngine');
eslintCLIEngineSpy.mockImplementation(() => ({
executeOnFiles: jest.fn().mockImplementation(() => ({
errorCount: 0,
results: {
length: Number.POSITIVE_INFINITY
}
})),
resolveFileGlobPatterns: jest.fn()
}));

// For happy paths, mock the file existing.
existsSyncSpy = jest.spyOn(fs, 'existsSync');
existsSyncSpy.mockImplementation(() => true);

mockConsoleLog = jest.spyOn(console, 'log');
mockConsoleLog.mockImplementation(noop);

mockConsoleWarn = jest.spyOn(console, 'warn');
mockConsoleWarn.mockImplementation(noop);

mockProcessExit = jest.spyOn(process, 'exit');
mockProcessExit.mockImplementation(noop);
});
afterEach(() => {
eslintCLIEngineSpy.mockClear();
existsSyncSpy.mockClear();
mockConsoleLog.mockClear();
mockConsoleWarn.mockClear();
mockProcessExit.mockClear();
});
afterAll(() => {
eslintCLIEngineSpy.mockRestore();
existsSyncSpy.mockRestore();
mockConsoleLog.mockRestore();
mockConsoleWarn.mockRestore();
mockProcessExit.mockRestore();
});

test('it is a function', () => {
expect(plugin.handler).toBeInstanceOf(Function);
});

test('it returns undefined', async () => {
const actual = await plugin.handler(mockContext, mockArgs);

expect(actual).toBeUndefined();
});

test("it throws if the schema doesn't exist locally", async () => {
// Mock the file not existing.
existsSyncSpy.mockImplementationOnce(() => false);

await plugin.handler(mockContext, mockArgs);

expect(existsSyncSpy).toHaveBeenCalled();
expect(mockContext.spinner.fail).toHaveBeenCalled();
expect(mockProcessExit).toHaveBeenCalledWith(1);
});

test('it creates a validator with the correct configuration', async () => {
const expectedRule = [
'error',
// These objects are derived from mockArgs.
{
env: 'apollo',
projectName: 'myApp'
},
{
env: 'literal',
projectName: 'myApp'
}
];

await plugin.handler(mockContext, mockArgs);

const lintConfiguration = eslintCLIEngineSpy.mock.calls[0][0];

const keys = Object.keys(lintConfiguration);
expect(keys).toHaveLength(4);
expect(keys).toContain('parser');
expect(keys).toContain('plugins');
expect(keys).toContain('rules');
expect(keys).toContain('useEslintrc');

expect(lintConfiguration.parser).toBe('babel-eslint');

expect(lintConfiguration.plugins).toHaveLength(1);
expect(lintConfiguration.plugins).toContain('graphql');

const rulesKeys = Object.keys(lintConfiguration.rules);
expect(rulesKeys).toHaveLength(2);
expect(rulesKeys).toContain('graphql/template-strings');
expect(rulesKeys).toContain('graphql/no-deprecated-fields');

const templateStringsRule =
lintConfiguration.rules['graphql/template-strings'];
expect(templateStringsRule).toEqual(expectedRule);

const deprecatedFieldsRule =
lintConfiguration.rules['graphql/no-deprecated-fields'];
expect(deprecatedFieldsRule).toEqual(expectedRule);

expect(lintConfiguration.useEslintrc).toBe(false);
});

test('it logs an appropriate message when there are no errors', async () => {
await plugin.handler(mockContext, mockArgs);

expect(mockConsoleLog).toHaveBeenCalled();
expect(mockProcessExit).toHaveBeenCalledWith(0);
});

test('it warns when there are errors', async () => {
eslintCLIEngineSpy.mockImplementationOnce(() => ({
executeOnFiles: jest.fn().mockImplementation(() => ({
errorCount: Number.POSITIVE_INFINITY,
results: {
length: Number.POSITIVE_INFINITY
}
})),
getFormatter: jest.fn().mockImplementation(() => {
return function() {};
}),
resolveFileGlobPatterns: jest.fn()
}));

await plugin.handler(mockContext, mockArgs);

expect(mockConsoleWarn).toHaveBeenCalled();
expect(mockProcessExit).toHaveBeenCalledWith(1);
});
});
Loading

0 comments on commit a156a48

Please sign in to comment.