Skip to content

Commit a8b43ee

Browse files
feat(app): render spec list, command log, iframe (#18372)
* wip * wip * wip * pre-bundle deps * wip * wip: spec * clear iframe * wip: it kind of works * wip, docs * wip * update runner API * wip * remove comments * do not render header * revert some stuff * remove test * update tests to work with react 17 * types * wip * revert react deps * move code around * add back hash change code * lint * comment * update readme * remove commet * revert yarn lock * remove unused file * Delete HelloWorld1.spec.tsx * Delete HelloWorld2.spec.tsx * update types * fix ui * fix bugs * fix test * remove dead code * more dead code Co-authored-by: Jessica Sachs <jess@jessicasachs.io>
1 parent 6610083 commit a8b43ee

38 files changed

+674
-255
lines changed

packages/app/README.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,25 @@ This is the front-end for the Cypress App.
44

55
## Development
66

7-
1. Use existing project to get a server (for example `cd packages/runner-ct && yarn cypress:open`)
8-
2. It will open in a new browser on port 8080
9-
3. Do `yarn start`. It will start the front-end for the new Cypress app
10-
4. To back to the browser opened in step 2
11-
5. Visit http://localhost:8080/__vite__/ for the new front-end powered by Vite (currently running the RunnerCt)
7+
1. `yarn dev` (inside of `packages/app`)
8+
2. It will open launchpad
9+
3. Select Component Testing (current E2E is not fully working)
10+
3. Open chrome (or another browser)
11+
4. This launches the existing CT Runner. Change the URL to http://localhost:3000/__vite__/ (note the trailing `/`)
12+
5. It should show the new Vite powered app
13+
14+
## Using existing, Vite-incompatible modules
15+
16+
Some of our modules, like `@packages/reporter`, `@packages/driver` and `@packages/runner-shared` cannot be easily
17+
used with Vite due to circular dependencies and modules that do not have compatible ESM builds.
18+
19+
To work around this, when consuming existing code, it is bundled with webpack and made available under the
20+
`window.UnifiedRunner` namespace. It is injected via [`injectBundle`](./src/runner/injectBundle.ts).
21+
22+
To add more code to the bundle, add it in the bundle root, `@packages/runner-ct/src/main.tsx` and attach it to
23+
`window.UnifiedRunner`.
24+
25+
As a rule of thumb, avoid importing from the older, webpack based modules into this package. Instead, if you want to consume code from those older, webpack bundled modules, you should add them to the webpack root and consume them via `window.UnifiedRunner`. Ideally, update [`index.d.ts`](./index.d.ts) to add the types, as well.
1226

1327
### Icons
1428

@@ -63,3 +77,4 @@ You can see this in the video with the Settings cog. It uses and `evenodd` fill
6377
## Diagram
6478

6579
![](./unified-runner-diagram.png)]
80+
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
describe('App', () => {
22
it('resolves the home page', () => {
33
cy.visit('http://localhost:5556')
4-
cy.get('[href="#/runs"]').click()
5-
cy.get('[href="#/settings"]').click()
4+
cy.get('[href="/__vite__/runner"]').click()
5+
cy.get('[href="/__vite__/settings"]').click()
66
})
77
})

packages/app/index.d.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import type { Store } from './src/store'
2+
3+
interface ConnectionInfo {
4+
automationElement: '__cypress-string',
5+
randomString: string
6+
}
7+
8+
export {}
9+
10+
/**
11+
* The eventManager and driver are bundled separately
12+
* by webpack. We cannot import them because of
13+
* circular dependencies.
14+
* To work around this, we build the driver, eventManager
15+
* and some other dependencies using webpack, and consumed the dist'd
16+
* source code.
17+
*
18+
* This is attached to `window` under the `UnifiedRunner` namespace.
19+
*
20+
* For now, just declare the types that we need to give us type safety where possible.
21+
* Eventually, we should decouple the event manager and import it directly.
22+
*/
23+
declare global {
24+
interface Window {
25+
UnifiedRunner: {
26+
/**
27+
* decode config, which we receive as a base64 string
28+
* This comes from Driver.utils
29+
*/
30+
decodeBase64: (base64: string) => Record<string, unknown>
31+
32+
/**
33+
* Proxy event to the reporter via `Reporter.defaultEvents.emit`
34+
*/
35+
emit (evt: string, ...args: unknown[]): void
36+
37+
/**
38+
* This is the eventManager which orchestrates all the communication
39+
* between the reporter, driver, and server, as well as handle
40+
* setup, teardown and running of specs.
41+
*
42+
* It's only used on the "Runner" part of the unified runner.
43+
*/
44+
eventManager: {
45+
addGlobalListeners: (state: Store, connectionInfo: ConnectionInfo) => void
46+
setup: (config: Record<string, unknown>) => void
47+
initialize: ($autIframe: JQuery<HTMLIFrameElement>, config: Record<string, unknown>) => void
48+
teardown: (state: Store) => Promise<void>
49+
teardownReporter: () => Promise<void>
50+
[key: string]: any
51+
}
52+
53+
/**
54+
* To ensure we are only a single copy of React
55+
* We get a reference to the copy of React (and React DOM)
56+
* that is used in the Reporter and Driver, which are bundled with
57+
* webpack.
58+
*
59+
* Unfortunately, attempting to have React in a project
60+
* using Vue causes mad conflicts because React'S JSX type
61+
* is ambient, so we cannot actually type it.
62+
*/
63+
React: any
64+
ReactDOM: any
65+
66+
/**
67+
* Any React components or general code needed from
68+
* runner-shared, reporter or driver are also bundled with
69+
* webpack and made available via the window.UnifedRunner namespace.
70+
*
71+
* We cannot import the correct types, because this causes the linter and type
72+
* checker to run on runner-shared and reporter, and it blows up.
73+
*/
74+
AutIframe: any
75+
Reporter: any
76+
}
77+
}
78+
}

packages/app/src/App.vue

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
<template>
22
<router-view v-slot="{ Component }">
3-
<keep-alive>
4-
<component :is="Component" />
5-
</keep-alive>
3+
<!--
4+
TODO(lachlan): put this back after doing proper cleanup when unmounting the runner page
5+
keep-alive works fine for Vue but has some weird effects on the React based Reporter
6+
For now it's way more simple to just unmount and remount the components when changing page.
7+
-->
8+
<!-- <keep-alive> -->
9+
<component :is="Component" />
10+
<!-- </keep-alive> -->
611
</router-view>
712
</template>

packages/app/src/layouts/default.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
name="fade"
1717
mode="out-in"
1818
>
19-
<keep-alive>
20-
<component
21-
:is="Component"
22-
/>
23-
</keep-alive>
19+
<!-- <keep-alive> -->
20+
<component
21+
:is="Component"
22+
/>
23+
<!-- </keep-alive> -->
2424
</transition>
2525
</router-view>
2626
</section>

packages/app/src/navigation/SidebarNavigation.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,13 @@
1616
>
1717
<router-link
1818
v-for="item in navigation"
19-
v-slot="{ href, isActive }"
19+
v-slot="{ isActive }"
2020
:key="item.name"
21-
custom
2221
:to="item.href"
2322
>
2423
<SidebarNavigationRow
2524
:active="isActive"
2625
:icon="item.icon"
27-
:href="href"
2826
>
2927
{{ item.name }}
3028
</SidebarNavigationRow>
@@ -43,7 +41,7 @@ import SettingsIcon from '~icons/cy/settings_x24'
4341
4442
const navigation = [
4543
{ name: 'Specs', icon: SpecsIcon, href: '/' },
46-
{ name: 'Runs', icon: CodeIcon, href: '/runs' },
44+
{ name: 'Runs', icon: CodeIcon, href: '/runner' },
4745
{ name: 'Settings', icon: SettingsIcon, href: '/settings' },
4846
]
4947

packages/app/src/navigation/SidebarNavigationRow.vue

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<template>
2-
<a
3-
href="#"
2+
<div
43
:class="[active ? 'before:bg-jade-300' : 'before:bg-transparent']"
54
class="w-full
65
min-w-40px
@@ -43,16 +42,16 @@
4342
<slot />
4443
</span>
4544
</span>
46-
</a>
45+
</div>
4746
</template>
4847

4948
<script lang="ts" setup>
5049
import type { FunctionalComponent, SVGAttributes } from 'vue'
5150
5251
withDefaults(defineProps <{
53-
icon?: FunctionalComponent<SVGAttributes, {}>
52+
icon: FunctionalComponent<SVGAttributes, {}>
5453
// Currently active row (generally the current route)
55-
active?: boolean
54+
active: boolean
5655
}>(), {
5756
active: false,
5857
icon: undefined,

packages/app/src/pages/Index.vue

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
<template>
2-
<Specs />
2+
<div>
3+
<div v-if="query.fetching.value">
4+
Loading...
5+
</div>
6+
<div v-else-if="query.data.value?.app?.activeProject">
7+
<Specs :gql="query.data.value.app" />
8+
</div>
9+
</div>
310
</template>
411

512
<script setup lang="ts">
13+
import { gql, useQuery } from '@urql/vue'
614
import Specs from './Specs.vue'
15+
import { IndexDocument } from '../generated/graphql'
16+
17+
gql`
18+
query Index {
19+
app {
20+
...Specs_Specs
21+
}
22+
}`
23+
24+
const query = useQuery({ query: IndexDocument })
725
</script>
826

927
<route>

packages/app/src/pages/Runner/[...all].vue

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,49 @@
11
<template>
22
<div>
3-
<h2>Runs Page</h2>
4-
<h4 v-if="!ready">
5-
Loading
6-
</h4>
7-
<h4 v-else>
8-
Error: TODO add gql backend for getting the base64Config into the browser
9-
</h4>
3+
<h2>Runner Page</h2>
4+
5+
<div v-once>
6+
<div :id="RUNNER_ID" />
7+
<div :id="REPORTER_ID" />
8+
</div>
109
</div>
1110
</template>
1211

13-
<script setup lang="ts">
14-
import { ref } from 'vue'
15-
import { renderRunner } from '../../runner/renderRunner'
12+
<script lang="ts" setup>
13+
import { onMounted } from 'vue'
14+
import type { SpecFile } from '@packages/types/src'
15+
import { UnifiedRunnerAPI } from '../../runner'
16+
import { REPORTER_ID, RUNNER_ID } from '../../runner/utils'
17+
import { useRoute } from 'vue-router'
18+
19+
onMounted(() => {
20+
UnifiedRunnerAPI.initialize(executeSpec)
21+
})
1622
17-
const ready = ref(false)
23+
const route = useRoute()
1824
19-
// @ts-ignore
20-
window.__Cypress__ = true
25+
function executeSpec () {
26+
const absolute = route.hash.slice(1)
2127
22-
renderRunner(() => {
23-
setTimeout(function () {
28+
if (absolute) {
2429
// @ts-ignore
25-
window.Runner.start(document.getElementById('app'), '{{base64Config | safe}}')
26-
}, 0)
30+
execute({
31+
absolute,
32+
relative: `src/Basic.spec.tsx`,
33+
name: `Basic.spec.tsx`,
34+
})
35+
}
36+
}
2737
28-
ready.value = true
29-
})
38+
const execute = (spec?: SpecFile) => {
39+
if (!spec) {
40+
return
41+
}
42+
43+
UnifiedRunnerAPI.executeSpec(spec)
44+
}
3045
</script>
46+
3147
<route>
3248
{
3349
name: "Runner"

packages/app/src/pages/Specs.vue

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,42 @@
11
<template>
2-
<div>
3-
<h2>Specs Page</h2>
4-
<template v-if="query.data.value?.app">
5-
<SpecsList :gql="query.data.value?.app" />
6-
</template>
7-
<p v-else>
8-
Loading...
9-
</p>
10-
</div>
2+
<h2>Specs Page</h2>
3+
4+
<SpecsList
5+
v-if="props.gql"
6+
:gql="props?.gql"
7+
/>
8+
9+
<p v-else>
10+
Loading...
11+
</p>
1112
</template>
1213

1314
<script lang="ts" setup>
14-
import { gql } from '@urql/core'
15-
import { useQuery } from '@urql/vue'
16-
import { Specs_AppDocument } from '../generated/graphql'
15+
import { gql } from '@urql/vue'
1716
import SpecsList from '../specs/SpecsList.vue'
17+
import { REPORTER_ID, RUNNER_ID } from '../runner/utils'
18+
import type { Specs_SpecsFragment } from '../generated/graphql'
1819
1920
gql`
20-
query Specs_App {
21-
app {
22-
...SpecsList
23-
}
24-
}
25-
`
26-
27-
const query = useQuery({
28-
query: Specs_AppDocument,
29-
})
21+
fragment Specs_Specs on App {
22+
...Specs_SpecsList
23+
}`
3024
25+
const props = defineProps<{
26+
gql: Specs_SpecsFragment
27+
}>()
3128
</script>
29+
3230
<route>
3331
{
3432
name: "Specs Page"
3533
}
3634
</route>
35+
36+
<style>
37+
iframe {
38+
border: 5px solid black;
39+
margin: 10px;
40+
background: lightgray;
41+
}
42+
</style>

0 commit comments

Comments
 (0)