Files
posthog/plugin-server/tests/worker/transforms.test.ts
Thomas Obermüller 4a30e78b22 chore: use pnpm to manage dependencies (closes #12635) (#13190)
* chore: use pnpm to manage dependencies

* Fix CI errors

* Don't report Docker image size for external PRs

* Fix pnpm-lock.yaml formatting

* Fix module versions

* Ignore pnpm-lock.yaml

* Upgrade Cypress action for pnpm support

* Set up node and pnpm before Cypress

* Fix typescript issues

* Include patches directory in Dockerfile

* Fix Jest tests in CI

* Update lockfile

* Update lockfile

* Clean up Dockerfile

* Update pnpm-lock.yaml to reflect current package.json files

* remove yarn-error.log from .gitignore

* formatting

* update data exploration readme

* type jest.config.ts

* fix @react-hook issues for jest

* fix react-syntax-highlighter issues for jest

* fix jest issues from query-selector-shadow-dom

* fix transform ignore patterns and undo previous fixes

* add missing storybook peer dependencies

* fix nullish coalescing operator for storybook

* reorder storybook plugins

* update editor-update-tsd warning to new npm script

* use legacy ssl for chromatic / node 18 compatibility

* use pnpm for visual regression testing workflow

* use node 16 for chromatic

* add @babel/plugin-proposal-nullish-coalescing-operator as direct dependency

* try fix for plugin-server

* cleanup

* fix comment and warning

* update more comments

* update playwright dockerfile

* update plugin source types

* conditional image size reporting

* revert react-native instructions

* less restrictive pnpm verions

* use ref component name in line with style guide

Co-authored-by: Jacob Gillespie <jacobwgillespie@gmail.com>
2022-12-12 10:28:06 +01:00

258 lines
7.7 KiB
TypeScript

import { Hub } from '../../src/types'
import { createHub } from '../../src/utils/db/hub'
import { code } from '../../src/utils/utils'
import { transformCode } from '../../src/worker/vm/transforms'
import { resetTestDatabase } from '../helpers/sql'
jest.mock('../../src/utils/status')
describe('transforms', () => {
let hub: Hub
let closeHub: () => Promise<void>
beforeEach(async () => {
;[hub, closeHub] = await createHub()
await resetTestDatabase(`const processEvent = event => event`)
})
afterEach(async () => {
await closeHub()
})
describe('transformCode', () => {
it('secures awaits by wrapping promises in __asyncGuard', () => {
const rawCode = code`
async function x() {
await console.log()
}
`
const transformedCode = transformCode(rawCode, hub)
expect(transformedCode).toStrictEqual(code`
"use strict";
async function x() {
await __asyncGuard(console.log(), "await on line 2:2");
}
`)
})
it('attaches caller information to awaits', () => {
const rawCode = code`
async function x() {
await anotherAsyncFunction('arg1', 'arg2')
}
`
const transformedCode = transformCode(rawCode, hub)
expect(transformedCode).toStrictEqual(code`
"use strict";
async function x() {
await __asyncGuard(anotherAsyncFunction('arg1', 'arg2'), "await on line 2:2");
}
`)
})
it('attaches caller information to awaits for anonymous functions', () => {
const rawCode = code`
async function x() {
await (async () => {console.log()})
}
`
const transformedCode = transformCode(rawCode, hub)
expect(transformedCode).toStrictEqual(code`
"use strict";
async function x() {
await __asyncGuard(async () => {
console.log();
}, "await on line 2:2");
}
`)
})
it('secures then calls by wrapping promises in __asyncGuard', () => {
const rawCode = code`
async function x() {}
x.then(() => null)
`
const transformedCode = transformCode(rawCode, hub)
expect(transformedCode).toStrictEqual(code`
"use strict";
async function x() {}
__asyncGuard(x, "Promise.then on line 2:0").then(() => null);
`)
})
it('secures block for loops with timeouts', () => {
const rawCode = code`
for (let i = 0; i < i + 1; i++) {
console.log(i)
}
`
const transformedCode = transformCode(rawCode, hub)
expect(transformedCode).toStrictEqual(code`
"use strict";
const _LP = Date.now();
for (let i = 0; i < i + 1; i++) {
if (Date.now() - _LP > 30000) throw new Error("Script execution timed out after looping for 30 seconds on line 1:0");
console.log(i);
}
`)
})
it('secures inline for loops with timeouts', () => {
const rawCode = code`
for (let i = 0; i < i + 1; i++) console.log(i)
`
const transformedCode = transformCode(rawCode, hub)
expect(transformedCode).toStrictEqual(code`
"use strict";
const _LP = Date.now();
for (let i = 0; i < i + 1; i++) {
if (Date.now() - _LP > 30000) throw new Error("Script execution timed out after looping for 30 seconds on line 1:0");
console.log(i);
}
`)
})
it('secures block for loops with timeouts avoiding _LP collision', () => {
const rawCode = code`
const _LP = 0
for (let i = 0; i < i + 1; i++) {
console.log(i)
}
`
const transformedCode = transformCode(rawCode, hub)
expect(transformedCode).toStrictEqual(code`
"use strict";
const _LP = 0;
const _LP2 = Date.now();
for (let i = 0; i < i + 1; i++) {
if (Date.now() - _LP2 > 30000) throw new Error("Script execution timed out after looping for 30 seconds on line 3:0");
console.log(i);
}
`)
})
it('transforms TypeScript to plain JavaScript', () => {
const rawCode = code`
interface Y {
a: int
b: string
}
function k({ a, b }: Y): string {
return \`a * 10 is {a * 10}, while b is just {b}\`
}
let a: int = 2
console.log(k({ a, b: 'tomato' }))
`
const transformedCode = transformCode(rawCode, hub)
expect(transformedCode).toStrictEqual(code`
"use strict";
function k({
a,
b
}) {
return \`a * 10 is {a * 10}, while b is just {b}\`;
}
let a = 2;
console.log(k({
a,
b: 'tomato'
}));
`)
})
it('replaces imports', () => {
const rawCode = code`
import { bla, bla2, bla3 as bla4 } from 'node-fetch'
import fetch1 from 'node-fetch'
import * as fetch2 from 'node-fetch'
console.log(bla, bla2, bla4, fetch1, fetch2);
`
const transformedCode = transformCode(rawCode, hub, { 'node-fetch': { bla: () => true } })
expect(transformedCode).toStrictEqual(code`
"use strict";
const bla = __pluginHostImports["node-fetch"]["bla"],
bla2 = __pluginHostImports["node-fetch"]["bla2"],
bla4 = __pluginHostImports["node-fetch"]["bla3"];
const fetch1 = __pluginHostImports["node-fetch"];
const fetch2 = __pluginHostImports["node-fetch"];
console.log(bla, bla2, bla4, fetch1, fetch2);
`)
})
it('only replaces provided imports', () => {
const rawCode = code`
import { kea } from 'kea'
console.log(kea)
`
expect(() => {
transformCode(rawCode, hub, { 'node-fetch': { default: () => true } })
}).toThrow("/index.ts: Cannot import 'kea'! This package is not provided by PostHog in plugins.")
})
it('replaces requires', () => {
const rawCode = code`
const fetch = require('node-fetch')
const { BigQuery } = require('@google-cloud/bigquery')
console.log(fetch, BigQuery);
`
const transformedCode = transformCode(rawCode, hub, {
'node-fetch': { bla: () => true },
'@google-cloud/bigquery': { BigQuery: () => true },
})
expect(transformedCode).toStrictEqual(code`
"use strict";
const fetch = __pluginHostImports["node-fetch"];
const {
BigQuery
} = __pluginHostImports["@google-cloud/bigquery"];
console.log(fetch, BigQuery);
`)
})
it('only replaces provided requires', () => {
const rawCode = code`
const { kea } = require('kea')
console.log(kea)
`
expect(() => {
transformCode(rawCode, hub, { 'node-fetch': { default: () => true } })
}).toThrow("/index.ts: Cannot import 'kea'! This package is not provided by PostHog in plugins.")
})
})
})