-
-
Notifications
You must be signed in to change notification settings - Fork 98
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: add Cypress #1094
feat: add Cypress #1094
Changes from 17 commits
8287ca2
902c44a
f98436d
385f0d6
8f1f7b0
e24606e
ad1d99a
3f72d69
dda3030
8c53284
c3baa62
961799a
53c6915
3553e8d
c505fcd
17c7a22
a07f335
c0f4083
2048b44
70264db
8583bb0
21e1755
1db3a5b
e66f922
bc3486b
5051992
0994c77
1fc8590
97fc975
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,4 +1,4 @@ | ||
# Disable specific file since it would introduce more complexity to reduce it - mainly code complexity and complex template literals | ||
sonar.exclusions=apps/studio/public/js/monaco/**,apps/studio/src/tailwind.css,apps/studio/src/components/SplitPane/** | ||
sonar.exclusions=apps/studio/public/js/monaco/**,apps/studio/src/tailwind.css,apps/studio/src/components/SplitPane/**,apps/studio-next/cypress/**,apps/studio-next/Dockerfile | ||
# Disable duplicate code in tests since it would introduce more complexity to reduce it. | ||
sonar.cpd.exclusions=apps/studio/**,apps/studio-next/src/components/Navigationv3.tsx,apps/studio-next/src/components/Navigation.tsx | ||
sonar.cpd.exclusions=apps/studio/**,apps/studio-next/src/components/Navigationv3.tsx,apps/studio-next/src/components/Navigation.tsx,apps/studio-next/cypress/** |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Dockerfile | ||
.dockerignore | ||
node_modules | ||
npm-debug.log | ||
README.md | ||
.next | ||
.git |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
FROM node:18-alpine AS base | ||
|
||
# Install dependencies only when needed | ||
FROM base AS deps | ||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. | ||
RUN apk add --no-cache libc6-compat | ||
WORKDIR /app | ||
|
||
# Install dependencies based on the preferred package manager | ||
COPY package.json ./ | ||
RUN npm install [email protected] --global | ||
# yarn.lock* package-lock.json* pnpm-lock.yaml* ./ | ||
RUN corepack enable pnpm && pnpm i | ||
|
||
# Rebuild the source code only when needed | ||
FROM base AS builder | ||
WORKDIR /app | ||
COPY --from=deps /app/node_modules ./node_modules | ||
COPY . . | ||
|
||
# Next.js collects completely anonymous telemetry data about general usage. | ||
# Learn more here: https://nextjs.org/telemetry | ||
# Uncomment the following line in case you want to disable telemetry during the build. | ||
ENV NEXT_TELEMETRY_DISABLED 1 | ||
|
||
RUN corepack enable pnpm && pnpm run build | ||
|
||
# Production image, copy all the files and run next | ||
FROM base AS runner | ||
WORKDIR /app | ||
|
||
ENV NODE_ENV production | ||
# Uncomment the following line in case you want to disable telemetry during runtime. | ||
# ENV NEXT_TELEMETRY_DISABLED 1 | ||
|
||
RUN addgroup --system --gid 1001 nodejs | ||
RUN adduser --system --uid 1001 nextjs | ||
|
||
COPY --from=builder /app/public ./public | ||
|
||
# Set the correct permission for prerender cache | ||
RUN mkdir .next | ||
RUN chown nextjs:nodejs .next | ||
|
||
# Automatically leverage output traces to reduce image size | ||
# https://nextjs.org/docs/advanced-features/output-file-tracing | ||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ | ||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static | ||
|
||
USER nextjs | ||
|
||
EXPOSE 3001 | ||
|
||
ENV PORT 3001 | ||
|
||
# server.js is created by next build from the standalone output | ||
# https://nextjs.org/docs/pages/api-reference/next-config-js/output | ||
CMD HOSTNAME="0.0.0.0" node server.js |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { defineConfig } from 'cypress'; | ||
|
||
export default defineConfig({ | ||
e2e: { | ||
baseUrl: 'http://localhost:3001', | ||
retries: { | ||
runMode: 1, | ||
openMode: 1, | ||
}, | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// import { isV3 } from "../../src/components/Sidebar"; | ||
const isV3Test = true; | ||
|
||
/* Testing commented hovers is impossible even with `cypress-real-events` so | ||
testing of these hovers is postponed until either Cypress has better support for | ||
`mouseover`/`mouseenter` events or the architecture of `Studio` is changed to | ||
allow testing those. */ | ||
|
||
describe('Studio UI spec', () => { | ||
beforeEach(() => { | ||
cy.visit('/'); | ||
}); | ||
|
||
it('Logo should be visible in the UI', () => { | ||
cy.get('[data-test="logo"]').should('be.visible'); | ||
}); | ||
|
||
// it('Logo should display tooltip "AsyncAPI Logo" on hover', () => { | ||
// cy.get('[data-test="logo"]').trigger('mouseenter'); | ||
// cy.contains('AsyncAPI Logo').should('be.visible'); | ||
// }); | ||
|
||
it('Button "AsyncAPI Website" should be visible in the UI', () => { | ||
cy.get('[data-test="button-website"]').should('be.visible'); | ||
}); | ||
|
||
// it('Button "AsyncAPI Website" should display tooltip "AsyncAPI Website" on hover', () => { | ||
// cy.get('[data-test="button-website"]').trigger('mouseenter'); | ||
// cy.contains('AsyncAPI Website').should('be.visible'); | ||
// }); | ||
|
||
it('Button "AsyncAPI Github Organization" should be visible in the UI', () => { | ||
cy.get('[data-test="button-github"]').should('be.visible'); | ||
}); | ||
|
||
// it('Button "AsyncAPI Github Organization" should display tooltip "AsyncAPI Github Organization" on hover', () => { | ||
// cy.get('[data-test="button-github"]').trigger('mouseenter'); | ||
// cy.contains('AsyncAPI Github Organization').should('be.visible'); | ||
// }); | ||
|
||
it('Button "AsyncAPI Slack Workspace" should be visible in the UI', () => { | ||
cy.get('[data-test="button-slack"]').should('be.visible'); | ||
}); | ||
|
||
// it('Button "AsyncAPI Slack Workspace" should display tooltip "AsyncAPI Slack Workspace" on hover', () => { | ||
// cy.get('[data-test="button-slack"]').trigger('mouseenter'); | ||
// cy.contains('AsyncAPI Slack Workspace').should('be.visible'); | ||
// }); | ||
|
||
it('Button "Navigation" should be visible in the UI', () => { | ||
cy.get('[data-test="button-navigation"]').should('be.visible'); | ||
}); | ||
|
||
it('Button "Navigation" should display tooltip "Navigation" on hover', () => { | ||
cy.get('[data-test="button-navigation"]').trigger('mouseenter'); | ||
cy.contains('Navigation').should('be.visible'); | ||
}); | ||
|
||
it('Button "Editor" should be visible in the UI', () => { | ||
cy.get('[data-test="button-editor"]').should('be.visible'); | ||
}); | ||
|
||
it('Button "Editor" should display tooltip "Editor" on hover', () => { | ||
cy.get('[data-test="button-editor"]').trigger('mouseenter'); | ||
cy.contains('Editor').should('be.visible'); | ||
}); | ||
|
||
it('Button "Template preview" should be visible in the UI', () => { | ||
cy.get('[data-test="button-template-preview"]').should('be.visible'); | ||
}); | ||
|
||
it('Button "Template preview" should display tooltip "Template preview" on hover', () => { | ||
cy.get('[data-test="button-template-preview"]').trigger('mouseenter'); | ||
cy.contains('Template preview').should('be.visible'); | ||
}); | ||
|
||
if (!isV3Test) { // review the need of v2 testing in general | ||
it('Button "Blocks visualiser" should be visible in the UI', () => { | ||
cy.get('[data-test="button-blocks-visualiser"]').should('be.visible'); | ||
}); | ||
|
||
it('Button "Blocks visualiser" should display tooltip "Blocks visualiser" on hover', () => { | ||
cy.get('[data-test="button-blocks-visualiser"]').trigger('mouseenter'); | ||
cy.contains('Blocks visualiser').should('be.visible'); | ||
}); | ||
} | ||
|
||
it('Button "New file" should be visible in the UI', () => { | ||
cy.get('[data-test="button-new-file"]').should('be.visible'); | ||
}); | ||
|
||
it('Button "New file" should display tooltip "New file" on hover', () => { | ||
cy.get('[data-test="button-new-file"]').trigger('mouseenter'); | ||
cy.contains('New file').should('be.visible'); | ||
}); | ||
|
||
it('Button "Settings" should be visible in the UI', () => { | ||
cy.get('[data-test="button-settings"]').should('be.visible'); | ||
}); | ||
|
||
it('Button "Settings" should display tooltip "Studio settings" on hover', () => { | ||
cy.get('[data-test="button-settings"]').trigger('mouseenter'); | ||
cy.contains('Studio settings').should('be.visible'); | ||
}); | ||
|
||
it('Button "Share" should be visible in the UI', () => { | ||
cy.get('[data-test="button-share"]').should('be.visible'); | ||
}); | ||
|
||
it('Button "Share" should display tooltip "Share link" on hover', () => { | ||
cy.get('[data-test="button-share"]').trigger('mouseenter'); | ||
cy.contains('Share link').should('be.visible'); | ||
}); | ||
|
||
it('Button "Dropdown" should be visible in the UI', () => { | ||
cy.get('[data-test="button-dropdown"]').should('be.visible'); | ||
}); | ||
|
||
it('Dropdown menu should contain 8 elements with predefined text', () => { | ||
cy.get('[data-test="button-dropdown"]').click(); | ||
cy.contains('Import from URL'); | ||
cy.contains('Import File'); | ||
cy.contains('Import from Base64'); | ||
cy.contains('Generate code/docs'); | ||
cy.contains('Save as YAML'); | ||
cy.contains('Convert and save as JSON'); | ||
cy.contains('Convert to JSON'); | ||
cy.contains('Convert document'); | ||
}); | ||
|
||
it('Click on Dropdown menu\'s element "Generate code/docs" should open Modal window "Generate code/docs based on your AsyncAPI Document"', () => { | ||
cy.get('[data-test="button-dropdown"]').click(); | ||
cy.contains('Generate code/docs').click(); | ||
cy.contains('Generate code/docs based on your AsyncAPI Document'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"name": "Using fixtures to represent data", | ||
"email": "[email protected]", | ||
"body": "Fixtures are a great way to mock data for responses to routes" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/// <reference types="cypress" /> | ||
// *********************************************** | ||
// This example commands.ts shows you how to | ||
// create various custom commands and overwrite | ||
// existing commands. | ||
// | ||
// For more comprehensive examples of custom | ||
// commands please read more here: | ||
// https://on.cypress.io/custom-commands | ||
// *********************************************** | ||
// | ||
// | ||
// -- This is a parent command -- | ||
// Cypress.Commands.add('login', (email, password) => { ... }) | ||
// | ||
// | ||
// -- This is a child command -- | ||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) | ||
// | ||
// | ||
// -- This is a dual command -- | ||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) | ||
// | ||
// | ||
// -- This will overwrite an existing command -- | ||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) | ||
// | ||
// declare global { | ||
// namespace Cypress { | ||
// interface Chainable { | ||
// login(email: string, password: string): Chainable<void> | ||
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element> | ||
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element> | ||
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element> | ||
// } | ||
// } | ||
// } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// *********************************************************** | ||
// This example support/e2e.ts is processed and | ||
// loaded automatically before your test files. | ||
// | ||
// This is a great place to put global configuration and | ||
// behavior that modifies Cypress. | ||
// | ||
// You can change the location of this file or turn off | ||
// automatically serving support files with the | ||
// 'supportFile' configuration option. | ||
// | ||
// You can read more here: | ||
// https://on.cypress.io/configuration | ||
// *********************************************************** | ||
|
||
// Import commands.js using ES2015 syntax: | ||
import './commands' | ||
|
||
// Alternatively you can use CommonJS syntax: | ||
// require('./commands') |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,14 @@ | |
"dev": "next dev -p 3001", | ||
"build": "next build", | ||
"start": "next start", | ||
"lint": "echo 'No linting configured'" | ||
"lint": "next lint", | ||
"docker:build": "docker build -t asyncapi/studio:latest .", | ||
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. docker build should be done at the root of the project (so turborepo is able to prune the unwanted packages). maybe something like: 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. Simply making sure that Turborepo will not start to be turned into a monolith again after this change. Might it be better, just in case, to keep possibility to dockerize each app separately with Docker Compose, like it's done in https://github.com/dhaaana/turborepo-with-docker:
? 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. If it's possible to publish the separate docker image after this, I have no issue with it. 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.
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. I changed approach in favor of pushing into the Docker image the already compiled Please try running
in the root of the repo and check. The generated Docker image (became 294MB) is run with
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. Upon some reflection I think my tone might have come across as too critical and for that I apologize. My intentions were to provide technical feedback not to criticize you or your efforts personally. I know you have worked hard on the issue and I don't think it was just for you to be suspended in the bounty program or not getting the reward for this bounty issue. If you think it's best to remove the docker and merge the remaining code, let's do that. 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.
I cleaned the project reinstalled the packages and built the image with The size now shows up correctly. 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. What do you think about reusing the same compiled code for two simultaneous deployments now?
This process will guarantee that there is one same 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.
Just wanted to clarify all my concerns. If they are invalid we can definitely go ahead with this approach. Building in One Environment and Running in Another Is Generally a Bad IdeaAs you know, we are using linux to run random workflows. However, when it comes to publishing or testing, we are using a matrix of (Linux, Windows, and Mac) to build, publish or test our code. Why? Because we want to make sure that it is built within the environment that it is going to run on. How can you be sure that if you build something on Linux, it will run on Windows? You might argue that they are different since docker has it's own Engine on top of the OS and it will work. You might be right, but we need to test that theory. Is it worth it to go though the trouble, I don't think so. That's why building in the Docker image itself is always better. TransparencyWhen I look at the Dockerfile, I should be able to see that yes, I am actually downloading the source code and building it myself on my machine. This gives me peace of mind that nobody is pushing some fishy code into my environment. Most of the ecosystem now doesn't trust pre-built packages because of incidents like the XZ fiasco. Docker CacheAs I mentioned previously, Docker is really smart when it comes to building and running your image. If you follow best practices, it can help your package be as slim as possible. For example, if you haven't changed your package.json or lock file, why would you want to install your packages from scratch? Well, you don't want that. You want to get the source code, which is probably a few MB, use the cached version of the package installation layer, and build on top of that, right? Well, Docker is going to do exactly that. But if you are pushing an already built 290MB of code in a single layer, Docker won't be able to do anything. Even if the next version only changes by one byte, it has to invalidate that layer and install the entire 290MB of code again. So in a Dockerfile, it is always better to break down your build, installation, and run layers as much as you can. More layers mean Docker is able to cache more efficiently when Harder to MaintainIt is hard to maintain a separate and custom workflow to do things. People expect simplicity. If I want to build an image, I would naturally expect the If you build one more layer of abstraction on top and use a script, it is suspicious to say the least. Contributors/Users will have to check what exactly you are doing in that script and why they can't just build it with a simple 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. Thanks for investing time in explaining all that. |
||
"cy:e2e:chrome": "cypress run --e2e --browser chrome", | ||
"cy:e2e:chromium": "cypress run --e2e --browser chromium", | ||
"cy:e2e:edge": "cypress run --e2e --browser edge", | ||
"cy:e2e:electron": "cypress run --e2e --browser electron", | ||
"cy:e2e:firefox": "cypress run --e2e --browser firefox", | ||
"cy:open": "cypress open" | ||
}, | ||
"dependencies": { | ||
"@asyncapi/avro-schema-parser": "^3.0.19", | ||
|
@@ -30,12 +37,10 @@ | |
"react-icons": "^4.6.0", | ||
"reactflow": "^11.2.0", | ||
"@stoplight/yaml": "^4.3.0", | ||
"@codemirror/view": "^6.26.3", | ||
"@types/node": "20.4.6", | ||
"@types/react": "18.2.18", | ||
"@types/react-dom": "18.2.7", | ||
"autoprefixer": "10.4.14", | ||
"codemirror": "^6.0.1", | ||
"eslint-config-next": "13.4.12", | ||
"next": "14.2.3", | ||
"postcss": "8.4.31", | ||
|
@@ -72,6 +77,7 @@ | |
"autoprefixer": "^10.4.13", | ||
"browserify-zlib": "^0.2.0", | ||
"buffer": "^6.0.3", | ||
"cypress": "^13.10.0", | ||
"eslint-config-next": "14.1.4", | ||
"eslint-plugin-security": "^1.5.0", | ||
"eslint-plugin-sonarjs": "^0.16.0", | ||
|
@@ -88,5 +94,6 @@ | |
"util": "^0.12.5", | ||
"web-vitals": "^3.1.0", | ||
"webpack": "^5.75.0" | ||
} | ||
}, | ||
"packageManager": "[email protected]" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { NextResponse } from 'next/server'; | ||
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. Not sure if we can use Server Actions instead 🤔 ? 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. I searched the Internet back and forth and couldn't find answers to several simple questions. Server Actions are meant for performing data fetching and mutations on the Next.js Server. Taking this into account, does Server Action even make sense in this case, or should the structure with one API route remain (for now, the final architecture of the Please correct me if I'm getting something wrong in the new for me concept of Server Actions. 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.
I still new to this Reace Server Components (RSC), so I will answers as much as I know
The thing about Api Route which their rebranding as Route Handler is not supposed to be replaced by Server Actions but different type options in the Architecture which would be considered during the design https://www.youtube.com/watch?v=PtX5PF17tlQ
Server Actions are supposed to work on top of the React Server Component(RSC) but the majority of the applications inside StudioWrapper do not leverage RSC even for NextJS features like SSR/SSG do not work there Static Side Generator -> render JSX to HTML during built time This one of the good resources Dan Abramov explains what are the responsibilities of SSR(What NextJS did) and RSC, and how they work together interchangeably 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. @jerensl @KhudaDad414 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. @aeworxet you are right, we first need to decide how we are going to architecture the component for the |
||
|
||
export async function POST(request: Request) { | ||
const response = await request.json(); | ||
return new NextResponse(`route POST -> ${JSON.stringify(response)}`, {status: 201}); | ||
} |
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.
IMHO it is better to follow https://turbo.build/repo/docs/guides/tools/docker and create a docker image that way. it takes forever to install the dependencies (more than 30 minutes). I think it's because we are not leveraging
turbo prune
to get rid of the unwanted packages.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.
Another point: It takes forever to run post-install scripts of Cypress (I think it installs all of the browsers with each install, like Puppeteer). there should be a way to disable that when building a container. something like
--ignore-scripts
in npm. 🤔