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

NEBULA-1385: Add initial state to embeddable sandbox and HTML unit tests #6633

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1,355 changes: 978 additions & 377 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -91,6 +91,7 @@
"jest-junit": "14.0.0",
"jest-mock": "28.1.1",
"jest-mock-random": "1.1.1",
"jest-serializer-html": "^7.1.0",
"lodash.sumby": "4.6.0",
"nock": "13.2.8",
"node-fetch": "2.6.7",
Expand Down
@@ -0,0 +1,83 @@
import { getEmbeddedExplorerHTML } from '../../../plugin/landingPage/default/getEmbeddedHTML';
import type { ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions } from '../../../plugin/landingPage/default/types';

const version = '_latest';
expect.addSnapshotSerializer(require('jest-serializer-html'));

describe('Embedded Explorer Landing Page Config HTML', () => {
it('with document, variables, headers and displayOptions provided', () => {
const config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions =
{
includeCookies: true,
document: 'query Test { id }',
variables: {
option: {
a: 'val',
b: 1,
c: true,
},
},
headers: { authorization: 'true' },
embed: {
displayOptions: {
showHeadersAndEnvVars: true,
docsPanelState: 'open',
theme: 'light',
},
persistExplorerState: true,
},
graphRef: 'graph@current',
};
expect(getEmbeddedExplorerHTML(version, config)).toMatchInlineSnapshot(`
<style>
iframe {
background-color: white;
}
</style>
<div style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableExplorer"
>
</div>
<script src="https://embeddable-explorer.cdn.apollographql.com/_latest/embeddable-explorer.umd.production.min.js">
</script>
<script>
var endpointUrl = window.location.href;
var embeddedExplorerConfig = {"includeCookies":true,"document":"query Test { id }","variables":{"option":{"a":"val","b":1,"c":true}},"headers":{"authorization":"true"},"embed":{"displayOptions":{"showHeadersAndEnvVars":true,"docsPanelState":"open","theme":"light"},"persistExplorerState":true},"graphRef":"graph@current","target":"#embeddableExplorer","initialState":{"includeCookies":true,"document":"query Test { id }","variables":{"option":{"a":"val","b":1,"c":true}},"headers":{"authorization":"true"},"embed":{"displayOptions":{"showHeadersAndEnvVars":true,"docsPanelState":"open","theme":"light"},"persistExplorerState":true},"graphRef":"graph@current","displayOptions":{"showHeadersAndEnvVars":true,"docsPanelState":"open","theme":"light"}},"persistExplorerState":true};
new window.EmbeddedExplorer({
...embeddedExplorerConfig,
endpointUrl,
});
</script>
`);
});

it('for embedded explorer with document, variables, headers and displayOptions excluded', () => {
const config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions =
{
includeCookies: false,
embed: true as true,
graphRef: 'graph@current',
};
expect(getEmbeddedExplorerHTML(version, config)).toMatchInlineSnapshot(`
<style>
iframe {
background-color: white;
}
</style>
<div style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableExplorer"
>
</div>
<script src="https://embeddable-explorer.cdn.apollographql.com/_latest/embeddable-explorer.umd.production.min.js">
</script>
<script>
var endpointUrl = window.location.href;
var embeddedExplorerConfig = {"includeCookies":false,"embed":true,"graphRef":"graph@current","target":"#embeddableExplorer","initialState":{"includeCookies":false,"embed":true,"graphRef":"graph@current","displayOptions":{}},"persistExplorerState":false};
new window.EmbeddedExplorer({
...embeddedExplorerConfig,
endpointUrl,
});
</script>
`);
});
});
@@ -0,0 +1,74 @@
import { getEmbeddedSandboxHTML } from '../../../plugin/landingPage/default/getEmbeddedHTML';
import type { LandingPageConfig } from '../../../plugin/landingPage/default/types';

const version = '_latest';
expect.addSnapshotSerializer(require('jest-serializer-html'));

describe('Landing Page Config HTML', () => {
it('for embedded sandbox with document, variables and headers provided', () => {
const config: LandingPageConfig = {
includeCookies: true,
document: 'query Test { id }',
variables: {
option: {
a: 'val',
b: 1,
c: true,
},
},
headers: { authorization: 'true' },
embed: true,
};
expect(getEmbeddedSandboxHTML(version, config)).toMatchInlineSnapshot(`
<style>
iframe {
background-color: white;
}
</style>
<div style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableSandbox"
>
</div>
<script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js">
</script>
<script>
var initialEndpoint = window.location.href;
new window.EmbeddedSandbox({
target: '#embeddableSandbox',
initialEndpoint,
includeCookies: true,
initialState: {"document":"query Test { id }","variables":{"option":{"a":"val","b":1,"c":true}},"headers":{"authorization":"true"}},
});
</script>
`);
});

it('for embedded sandbox with document, variables and headers excluded', () => {
const config: LandingPageConfig = {
includeCookies: false,
embed: true,
};
expect(getEmbeddedSandboxHTML(version, config)).toMatchInlineSnapshot(`
<style>
iframe {
background-color: white;
}
</style>
<div style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableSandbox"
>
</div>
<script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js">
</script>
<script>
var initialEndpoint = window.location.href;
new window.EmbeddedSandbox({
target: '#embeddableSandbox',
initialEndpoint,
includeCookies: false,
initialState: {},
});
</script>
`);
});
});
119 changes: 119 additions & 0 deletions packages/server/src/plugin/landingPage/default/getEmbeddedHTML.ts
@@ -0,0 +1,119 @@
import type {
ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions,
LandingPageConfig,
} from './types';

// This function turns an object into a string and replaces
// <, >, &, ' with their unicode chars to avoid adding html tags to
// the landing page html that might be passed from the config.
// The only place these characters can appear in the output of
// JSON.stringify is within string literals, where they can equally
// well appear \u-escaped. This specifically means that
// `</script>` won't terminate the script block early.
// (Perhaps we should have done this instead of the triple-encoding
// of encodeConfig for the main landing page.)
function getConfigStringForHtml(config: LandingPageConfig) {
return JSON.stringify(config)
.replace('<', '\\u003c')
.replace('>', '\\u003e')
.replace('&', '\\u0026')
.replace("'", '\\u0027');
}

export const getEmbeddedExplorerHTML = (
version: string,
config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions,
) => {
interface EmbeddableExplorerOptions {
graphRef: string;
target: string;

initialState?: {
document?: string;
variables?: Record<string, any>;
headers?: Record<string, string>;
displayOptions: {
docsPanelState?: 'open' | 'closed'; // default to 'open',
showHeadersAndEnvVars?: boolean; // default to `false`
theme?: 'dark' | 'light';
};
};
persistExplorerState?: boolean; // defaults to 'false'

endpointUrl: string;

includeCookies?: boolean; // defaults to 'false'
}
const productionLandingPageConfigOrDefault = {
displayOptions: {},
persistExplorerState: false,
...(typeof config.embed === 'boolean' ? {} : config.embed),
};
const embeddedExplorerParams: Omit<EmbeddableExplorerOptions, 'endpointUrl'> =
{
...config,
target: '#embeddableExplorer',
initialState: {
...config,
displayOptions: {
...productionLandingPageConfigOrDefault.displayOptions,
},
},
persistExplorerState:
productionLandingPageConfigOrDefault.persistExplorerState,
};

return `
<style>
iframe {
background-color: white;
}
</style>
<div
style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableExplorer"
></div>
<script src="https://embeddable-explorer.cdn.apollographql.com/${version}/embeddable-explorer.umd.production.min.js"></script>
<script>
var endpointUrl = window.location.href;
var embeddedExplorerConfig = ${getConfigStringForHtml(
embeddedExplorerParams,
)};
new window.EmbeddedExplorer({
...embeddedExplorerConfig,
endpointUrl,
});
</script>
`;
};

export const getEmbeddedSandboxHTML = (
version: string,
config: LandingPageConfig,
) => {
return `
<style>
iframe {
background-color: white;
}
</style>
<div
style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableSandbox"
></div>
<script src="https://embeddable-sandbox.cdn.apollographql.com/${version}/embeddable-sandbox.umd.production.min.js"></script>
<script>
var initialEndpoint = window.location.href;
new window.EmbeddedSandbox({
target: '#embeddableSandbox',
initialEndpoint,
includeCookies: ${config.includeCookies ?? 'false'},
initialState: ${getConfigStringForHtml({
document: config.document ?? undefined,
variables: config.variables ?? undefined,
headers: config.headers ?? undefined,
})},
});
</script>
`;
};