Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve(laboratory): validate editor content with TypeScript #6476

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .changeset/funny-poets-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
'hive': minor
---

Laboratory Preflight now validates your script with TypeScript. Also, the `WebWorker` runtime types are applied giving you confidence about what globals are available to you in your script.

## Backwards Incompatible Notes

This change is backwards incompatible in the sense that invalid or problematic Script code which would have previously not statically errored will now. However at this time we do not prevent script saving because of static type errors. Therefore your workflow should only at worst be visually impacted.

## About WebWorker Runtime & Types

To learn more about what the WebWorker runtime and types are, you can review the following:

1. https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
2. https://www.typescriptlang.org/tsconfig/#lib (see "WebWorker")


## Leveraging TypeScript in JavaScript

If you are not familiar with TypeScript, here is a tip for you if you find yourself with a TypeScript error that you cannot or do not want to fix. You can silence them by using comments:

```js
let a = 1;
let b = '';
// @ts-ignore
a = b;
// @ts-expect-error
a = b;
```

The advantage of `@ts-expect-error` is that if there is no error to ignore, then the comment itself becomes an error whereas `@ts-ignore` sits there quietly whether it has an effect or not.

There is more you can do with TypeScript in JavaScript, such as providing type annotations via JSDoc. Learn more about it all here:

https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html
90 changes: 90 additions & 0 deletions cypress/e2e/laboratory/__cypress__.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { cyMonaco } from '../../support/monaco';

export namespace cyLaboratory {
/**
* Updates the value of the graphiql editor
*/
export function updateEditorValue(value: string) {
cy.get('.graphiql-query-editor .cm-s-graphiql').then($editor => {
const editor = ($editor[0] as any).CodeMirror; // Access the CodeMirror instance
editor.setValue(value);
});
jasonkuhrt marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Returns the value of the graphiql editor as Chainable<string>
*/
export function getEditorValue() {
return cy.get('.graphiql-query-editor .cm-s-graphiql').then<string>($editor => {
const editor = ($editor[0] as any).CodeMirror; // Access the CodeMirror instance
return editor.getValue();
});
}

/**
* Opens a new tab
*/
export function openNewTab() {
cy.get('button[aria-label="New tab"]').click();
// tab's title should be "untitled" as it's a default name
cy.contains('button[aria-controls="graphiql-session"]', 'untitled').should('exist');
}

/**
* Asserts that the tab with the given name is active
*/
export function assertActiveTab(name: string) {
cy.contains('li.graphiql-tab-active > button[aria-controls="graphiql-session"]', name).should(
'exist',
);
}

/**
* Closes the active tab
*/
export function closeActiveTab() {
cy.get('li.graphiql-tab-active > button.graphiql-tab-close').click();
}

/**
* Closes all tabs until one is left
*/
export function closeTabsUntilOneLeft() {
cy.get('li.graphiql-tab').then($tabs => {
if ($tabs.length > 1) {
closeActiveTab();
// Recurse until there's only one tab left
return closeTabsUntilOneLeft();
}
});
}

export namespace preflight {
export const selectors = {
buttonGraphiQLPreflight: '[aria-label*="Preflight Script"]',
buttonModal: '[data-cy="preflight-modal-button"]',
buttonToggle: '[data-cy="toggle-preflight"]',
buttonHeaders: '[data-name="headers"]',
headersEditor: {
textArea: '.graphiql-editor-tool .graphiql-editor:last-child textarea',
},
graphiql: {
buttonExecute: '.graphiql-execute-button',
},

modal: {
buttonSubmit: '[data-cy="preflight-modal-submit"]',
scriptEditor: '[data-cy="preflight-editor"]',
variablesEditor: '[data-cy="env-editor"]',
},
};

export const setScriptEditorContent = (value: string) => {
cyMonaco.setContent(selectors.modal.scriptEditor, value);
};

export const setEnvironmentEditorContent = (value: string) => {
cyMonaco.setContent(selectors.modal.variablesEditor, value);
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { laboratory } from '../support/testkit';
import { cyLaboratory } from './__cypress__';

beforeEach(() => {
cy.clearAllLocalStorage().then(() => {
Expand All @@ -16,7 +16,7 @@ beforeEach(() => {
.first()
.click();
cy.get('[aria-label="Show Operation Collections"]').click();
laboratory.closeTabsUntilOneLeft();
cyLaboratory.closeTabsUntilOneLeft();
});
});
});
Expand Down Expand Up @@ -90,7 +90,7 @@ describe('Laboratory > Collections', () => {
name: 'collection-1',
description: 'Description 1',
});
laboratory.updateEditorValue(`query op1 { test }`);
cyLaboratory.updateEditorValue(`query op1 { test }`);
collections.saveCurrentOperationAs({
name: 'operation-1',
collectionName: 'collection-1',
Expand All @@ -103,7 +103,7 @@ describe('Laboratory > Collections', () => {
name: 'collection-1',
description: 'Description 1',
});
laboratory.updateEditorValue(`query op1 { test }`);
cyLaboratory.updateEditorValue(`query op1 { test }`);
collections.saveCurrentOperationAs({
name: 'operation-1',
collectionName: 'collection-1',
Expand All @@ -127,7 +127,7 @@ describe('Laboratory > Collections', () => {
name: 'collection-1',
description: 'Description 1',
});
laboratory.updateEditorValue(`query op1 { test }`);
cyLaboratory.updateEditorValue(`query op1 { test }`);
collections.saveCurrentOperationAs({
name: 'operation-1',
collectionName: 'collection-1',
Expand All @@ -151,7 +151,7 @@ describe('Laboratory > Collections', () => {
name: 'collection-1',
description: 'Description 1',
});
laboratory.updateEditorValue(`query op1 { test }`);
cyLaboratory.updateEditorValue(`query op1 { test }`);
collections.saveCurrentOperationAs({
name: 'operation-1',
collectionName: 'collection-1',
Expand All @@ -173,14 +173,14 @@ describe('Laboratory > Collections', () => {
name: 'collection-1',
description: 'Description 1',
});
laboratory.updateEditorValue(`query op1 { test }`);
cyLaboratory.updateEditorValue(`query op1 { test }`);
collections.saveCurrentOperationAs({
name: 'operation-1',
collectionName: 'collection-1',
});

laboratory.openNewTab();
laboratory.updateEditorValue(`query op2 { test }`);
cyLaboratory.openNewTab();
cyLaboratory.updateEditorValue(`query op2 { test }`);
collections.saveCurrentOperationAs({
name: 'operation-2',
collectionName: 'collection-1',
Expand All @@ -206,14 +206,14 @@ describe('Laboratory > Collections', () => {
description: 'Description 2',
});
collections.clickCollectionButton('collection-1');
laboratory.updateEditorValue(`query op1 { test }`);
cyLaboratory.updateEditorValue(`query op1 { test }`);
collections.saveCurrentOperationAs({
name: 'operation-1',
collectionName: 'collection-1',
});

laboratory.openNewTab();
laboratory.updateEditorValue(`query op2 { test }`);
cyLaboratory.openNewTab();
cyLaboratory.updateEditorValue(`query op2 { test }`);
collections.saveCurrentOperationAs({
name: 'operation-2',
collectionName: 'collection-2',
Expand Down Expand Up @@ -243,7 +243,7 @@ describe('Laboratory > Collections', () => {
return cy.visit(copiedUrl);
});

laboratory.assertActiveTab('operation-1');
laboratory.getEditorValue().should('contain', 'op1');
cyLaboratory.assertActiveTab('operation-1');
cyLaboratory.getEditorValue().should('contain', 'op1');
});
});
Loading
Loading