-
Notifications
You must be signed in to change notification settings - Fork 104
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
feat: preflight scripts for laboratory #5564
base: main
Are you sure you want to change the base?
Changes from 119 commits
e4b09bd
3bdcab2
a455613
aa5ecb8
db6f984
1362231
24c1f7f
97414e1
06c2ecc
5c8b2f0
1e1dd05
6c6fc1d
0b48243
7f0025e
23fe786
fc3966c
9276cb7
770a158
3f17764
06d5b96
816f92a
257860c
f06fed4
e9ffc80
2110c7d
6fbdd04
f942172
0c3a9f9
d20f454
d3a66bc
bb00d01
36f8c32
dd5b588
1510a01
527373b
14e8cf0
736b77c
aff9468
2469276
a54b13d
3f5a2d6
28342ba
7bda2e3
2cb4e2d
1a8b261
4e4bdde
ee89ae2
cdda687
2f36bfc
790b24a
ac56106
c9964e5
ed71480
b831de6
629a4ca
82cb5bc
5d46167
e2b22cb
95cc44b
b568fcf
0bc7397
6b08723
ffa0380
a45e9cc
2f865e4
f02835c
ca55d5b
6c735f9
ea79bc5
c0f6488
58739cd
0be4397
ab70d62
5838e82
6f5710f
a60373b
e40e9da
85b24a4
58cad81
f5dbd49
af6519a
3e38bb0
1676dd0
6e9b7ab
ebefade
dd0db87
7cf4904
4dde658
f992123
08a25f0
db92293
e22a428
02707a9
d9113e4
71bc1e0
83840c2
7244b44
85c8673
7b4fefe
cbca6d3
bee25f2
c61bbdb
8cc19ec
08f94fb
9c70421
417b693
ad56588
c7017be
a70beb6
da9228e
c022299
63a7e4f
d2ca542
895c545
661ee13
5b9cbc8
51b37b0
55ad962
1187c2f
fc178c0
46bc881
dec32b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,38 @@ | ||
import * as fs from 'node:fs'; | ||
// eslint-disable-next-line import/no-extraneous-dependencies -- cypress SHOULD be a dev dependency | ||
import fs from 'node:fs'; | ||
import { defineConfig } from 'cypress'; | ||
import { initSeed } from './integration-tests/testkit/seed'; | ||
|
||
if (!process.env.RUN_AGAINST_LOCAL_SERVICES) { | ||
const dotenv = await import('dotenv'); | ||
dotenv.config({ path: import.meta.dirname + '/integration-tests/.env' }); | ||
} | ||
|
||
const isCI = Boolean(process.env.CI); | ||
|
||
export const seed = initSeed(); | ||
|
||
export default defineConfig({ | ||
video: isCI, | ||
screenshotOnRunFailure: isCI, | ||
defaultCommandTimeout: 15_000, // sometimes the app takes longer to load, especially in the CI | ||
retries: 2, | ||
env: { | ||
POSTGRES_URL: 'postgresql://postgres:postgres@localhost:5432/registry', | ||
}, | ||
e2e: { | ||
setupNodeEvents(on) { | ||
on('task', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: this is executed on a different thread than the tests, so we cannot simply inject the whole seed object into the tests. For now, we only need these seeds. So it's fine. Later one we might want to investigate into another solution for injecting the seed. |
||
async seedTarget() { | ||
const owner = await seed.createOwner(); | ||
const org = await owner.createOrg(); | ||
const project = await org.createProject(); | ||
const slug = `${org.organization.slug}/${project.project.slug}/${project.target.slug}`; | ||
return { | ||
slug, | ||
refreshToken: owner.ownerRefreshToken, | ||
email: owner.ownerEmail, | ||
}; | ||
}, | ||
}); | ||
|
||
on('after:spec', (_, results) => { | ||
if (results && results.video) { | ||
// Do we have failures for any retry attempts? | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
beforeEach(() => { | ||
cy.clearLocalStorage().then(async () => { | ||
cy.task('seedTarget').then(({ slug, refreshToken }: any) => { | ||
cy.setCookie('sRefreshToken', refreshToken); | ||
|
||
cy.visit(`/${slug}/laboratory`); | ||
cy.get('[aria-label*="Preflight Script"]').click(); | ||
}); | ||
}); | ||
}); | ||
|
||
/** Helper function for setting the text within a monaco editor as typing manually results in flaky tests */ | ||
function setMonacoEditorContents(editorCyName: string, text: string) { | ||
// wait for textarea appearing which indicates monaco is loaded | ||
cy.dataCy(editorCyName).find('textarea'); | ||
cy.window().then(win => { | ||
// First, check if monaco is available on the main window | ||
const editor = (win as any).monaco.editor | ||
.getEditors() | ||
.find(e => e.getContainerDomNode().parentElement.getAttribute('data-cy') === editorCyName); | ||
|
||
// If Monaco instance is found | ||
if (editor) { | ||
editor.setValue(text); | ||
} else { | ||
throw new Error('Monaco editor not found on the window or frames[0]'); | ||
} | ||
}); | ||
} | ||
|
||
function setEditorScript(script: string) { | ||
setMonacoEditorContents('preflight-script-editor', script); | ||
} | ||
|
||
describe('Preflight Script', () => { | ||
it('mini script editor is read only', () => { | ||
cy.dataCy('toggle-preflight-script').click(); | ||
// Wait loading disappears | ||
cy.dataCy('preflight-script-editor-mini').should('not.contain', 'Loading'); | ||
// Click | ||
cy.dataCy('preflight-script-editor-mini').click(); | ||
// And type | ||
cy.dataCy('preflight-script-editor-mini').within(() => { | ||
cy.get('textarea').type('🐝', { force: true }); | ||
}); | ||
cy.dataCy('preflight-script-editor-mini').should( | ||
'have.text', | ||
'Cannot edit in read-only editor', | ||
); | ||
}); | ||
}); | ||
|
||
describe('Preflight Script Modal', () => { | ||
const script = 'console.log("Hello_world")'; | ||
const env = '{"foo":123}'; | ||
|
||
beforeEach(() => { | ||
cy.dataCy('preflight-script-modal-button').click(); | ||
setMonacoEditorContents('env-editor', env); | ||
}); | ||
|
||
it('save script and environment variables when submitting', () => { | ||
setEditorScript(script); | ||
cy.dataCy('preflight-script-modal-submit').click(); | ||
cy.dataCy('env-editor-mini').should('have.text', env); | ||
cy.dataCy('toggle-preflight-script').click(); | ||
cy.dataCy('preflight-script-editor-mini').should('have.text', script); | ||
cy.reload(); | ||
cy.get('[aria-label*="Preflight Script"]').click(); | ||
cy.dataCy('env-editor-mini').should('have.text', env); | ||
cy.dataCy('preflight-script-editor-mini').should('have.text', script); | ||
}); | ||
|
||
it('logs show console/error information', () => { | ||
setEditorScript(script); | ||
cy.dataCy('run-preflight-script').click(); | ||
cy.dataCy('console-output').should('contain', 'Log: Hello_world (Line: 1, Column: 1)'); | ||
|
||
setEditorScript( | ||
`console.info(1) | ||
console.warn(true) | ||
console.error('Fatal') | ||
throw new TypeError('Test')`, | ||
); | ||
|
||
cy.dataCy('run-preflight-script').click(); | ||
// First log previous log message | ||
cy.dataCy('console-output').should('contain', 'Log: Hello_world (Line: 1, Column: 1)'); | ||
// After the new logs | ||
cy.dataCy('console-output').should( | ||
'contain', | ||
[ | ||
'Info: 1 (Line: 1, Column: 1)', | ||
'Warn: true (Line: 2, Column: 1)', | ||
'Error: Fatal (Line: 3, Column: 1)', | ||
'TypeError: Test (Line: 4, Column: 7)', | ||
].join(''), | ||
); | ||
}); | ||
|
||
it('script execution updates environment variables', () => { | ||
setEditorScript(`lab.environment.set('my-test', "TROLOLOL")`); | ||
|
||
cy.dataCy('run-preflight-script').click(); | ||
cy.dataCy('env-editor').should( | ||
'include.text', | ||
// replace space with | ||
'{ "foo": 123, "my-test": "TROLOLOL"}'.replaceAll(' ', '\xa0'), | ||
); | ||
}); | ||
|
||
it('`crypto-js` can be used for generating hashes', () => { | ||
setEditorScript('console.log(lab.CryptoJS.SHA256("🐝"))'); | ||
cy.dataCy('run-preflight-script').click(); | ||
cy.dataCy('console-output').should('contain', 'Info: Using crypto-js version:'); | ||
cy.dataCy('console-output').should( | ||
'contain', | ||
'Log: d5b51e79e4be0c4f4d6b9a14e16ca864de96afe68459e60a794e80393a4809e8', | ||
); | ||
}); | ||
|
||
it('scripts can not use `eval`', () => { | ||
setEditorScript('eval()'); | ||
cy.dataCy('preflight-script-modal-submit').click(); | ||
cy.get('body').contains('Usage of dangerous statement like eval() or Function("").'); | ||
}); | ||
|
||
it('invalid code is rejected and can not be saved', () => { | ||
setEditorScript('🐝'); | ||
cy.dataCy('preflight-script-modal-submit').click(); | ||
cy.get('body').contains("[1:1]: Illegal character '}"); | ||
}); | ||
}); | ||
|
||
describe('Execution', () => { | ||
it('header placeholders are substituted with environment variables', () => { | ||
cy.dataCy('toggle-preflight-script').click(); | ||
cy.get('[data-name="headers"]').click(); | ||
cy.get('.graphiql-editor-tool .graphiql-editor:last-child textarea').type( | ||
'{ "__test": "{{foo}} bar {{nonExist}}" }', | ||
{ | ||
force: true, | ||
parseSpecialCharSequences: false, | ||
}, | ||
); | ||
cy.dataCy('env-editor-mini').within(() => { | ||
cy.get('textarea').type('{"foo":"injected"}', { | ||
force: true, | ||
parseSpecialCharSequences: false, | ||
}); | ||
}); | ||
cy.intercept('/api/lab/foo/my-new-project/development', req => { | ||
expect(req.headers.__test).to.equal('injected bar {{nonExist}}'); | ||
}); | ||
cy.get('body').type('{ctrl}{enter}'); | ||
}); | ||
|
||
it('executed script updates update env editor and substitute headers', () => { | ||
cy.dataCy('toggle-preflight-script').click(); | ||
cy.get('[data-name="headers"]').click(); | ||
cy.get('.graphiql-editor-tool .graphiql-editor:last-child textarea').type( | ||
'{ "__test": "{{foo}}" }', | ||
{ | ||
force: true, | ||
parseSpecialCharSequences: false, | ||
}, | ||
); | ||
cy.dataCy('preflight-script-modal-button').click(); | ||
setMonacoEditorContents('preflight-script-editor', `lab.environment.set('foo', 92)`); | ||
cy.dataCy('preflight-script-modal-submit').click(); | ||
cy.intercept('/api/lab/foo/my-new-project/development', req => { | ||
expect(req.headers.__test).to.equal('92'); | ||
}); | ||
cy.get('.graphiql-execute-button').click(); | ||
}); | ||
|
||
it('disabled script is not executed', () => { | ||
cy.get('[data-name="headers"]').click(); | ||
cy.get('.graphiql-editor-tool .graphiql-editor:last-child textarea').type( | ||
'{ "__test": "{{foo}}" }', | ||
{ | ||
force: true, | ||
parseSpecialCharSequences: false, | ||
}, | ||
); | ||
cy.dataCy('preflight-script-modal-button').click(); | ||
setMonacoEditorContents('preflight-script-editor', `lab.environment.set('foo', 92)`); | ||
setMonacoEditorContents('env-editor', `{"foo":10}`); | ||
|
||
cy.dataCy('preflight-script-modal-submit').click(); | ||
cy.intercept('/api/lab/foo/my-new-project/development', req => { | ||
expect(req.headers.__test).to.equal('10'); | ||
}); | ||
cy.get('.graphiql-execute-button').click(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es5", | ||
"lib": ["es5", "dom"], | ||
"target": "es2021", | ||
"lib": ["es2021", "dom"], | ||
"types": ["node", "cypress"] | ||
}, | ||
"include": ["**/*.ts"] | ||
"include": ["**/*.ts", "../integration-tests/testkit/**/*.ts"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -333,12 +333,10 @@ deployCloudFlareSecurityTransform({ | |
// Staging | ||
'staging.graphql-hive.com', | ||
'app.staging.graphql-hive.com', | ||
'lab-worker.staging.graphql-hive.com', | ||
'cdn.staging.graphql-hive.com', | ||
// Dev | ||
'dev.graphql-hive.com', | ||
'app.dev.graphql-hive.com', | ||
'lab-worker.dev.graphql-hive.com', | ||
Comment on lines
-336
to
-341
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no longer needed, we don't need to run on a different domain to isolate the worker. |
||
'cdn.dev.graphql-hive.com', | ||
], | ||
}); | ||
|
@@ -351,4 +349,4 @@ export const schemaApiServiceId = schema.service.id; | |
export const webhooksApiServiceId = webhooks.service.id; | ||
|
||
export const appId = app.deployment.id; | ||
export const publicIp = proxy!.status.loadBalancer.ingress[0].ip; | ||
export const publicIp = proxy.get()!.status.loadBalancer.ingress[0].ip; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,5 @@ export function deployProxy({ | |
service: usage.service, | ||
retriable: true, | ||
}, | ||
]) | ||
.get(); | ||
]); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we now use the integration test seed here which uses client preset.