Start adding e2e test for TS SDK

This commit is contained in:
Filip Sodić 2024-12-20 19:27:54 +01:00
parent 63c56d40a5
commit 2fd9cdfc0c
25 changed files with 425 additions and 0 deletions

View File

@ -0,0 +1,11 @@
.wasp/
node_modules/
# Ignore all dotenv files by default to prevent accidentally committing any secrets.
# To include specific dotenv files, use the `!` operator or adjust these rules.
.env
.env.*
# Don't ignore example dotenv files.
!.env.example
!.env.*.example

View File

@ -0,0 +1,4 @@
# Ignore editor tmp files
**/*~
**/#*#
.DS_Store

View File

@ -0,0 +1 @@
File marking the root of Wasp project.

View File

@ -0,0 +1,89 @@
import { App } from 'wasp-config'
const app = new App('waspComplexTest', {
title: 'waspComplexTest',
wasp: { version: '^0.15.2' },
});
app.auth({
userEntity: 'User',
methods: {
google: {}
},
onAuthFailedRedirectTo: '/login',
onBeforeSignup: { import: 'onBeforeSignup', from: '@src/auth/hooks.js' },
onAfterSignup: { import: 'onAfterSignup', from: '@src/auth/hooks.js' },
onBeforeOAuthRedirect: { import: 'onBeforeOAuthRedirect', from: '@src/auth/hooks.js' },
});
app.server({
setupFn: { importDefault: 'mySetupFunction', from: '@src/server/myServerSetupCode.js' },
});
app.client({
setupFn: { importDefault: 'myClientSetupFunction', from: '@src/client/myClientSetupCode.js' },
rootComponent: { importDefault: 'App', from: '@src/client/App.jsx' }
});
app.emailSender({
provider: 'SendGrid',
defaultFrom: { name: 'Hello', email: 'hello@itsme.com' }
});
const mainPage = app.page('MainPage', {
component: { import: 'MainPage', from: '@src/MainPage' }
});
app.route('RootRoute', { path: '/', to: mainPage });
app.job('mySpecialJob', {
executor: 'PgBoss',
perform: {
fn: { import: 'foo', from: '@src/server/jobs/bar.js' }
}
});
app.job('returnHelloJob', {
executor: 'PgBoss',
perform: {
fn: { import: 'returnHello', from: '@src/server/jobs/returnHello.js' }
},
entities: ['User']
});
app.action('mySpecialAction', {
fn: { import: 'foo', from: '@src/server/actions/bar.js' },
entities: ['User']
});
app.query('mySpecialQuery', {
fn: { import: 'foo', from: '@src/server/queries/bar.js' },
entities: ['User']
});
app.apiNamespace('fooBarNamespace', {
middlewareConfigFn: { import: 'fooBarNamespaceMiddlewareFn', from: '@src/server/apiNamespaces.js' },
path: '/bar'
});
app.api('fooBar', {
fn: { import: 'fooBar', from: '@src/server/apis.js' },
httpRoute: ['GET', '/foo/bar'],
middlewareConfigFn: { import: 'fooBarMiddlewareFn', from: '@src/server/apis.js' }
});
app.api('fooBaz', {
fn: { import: 'fooBaz', from: '@src/server/apis.js' },
httpRoute: ['GET', '/foo/baz'],
auth: false
});
app.crud('tasks', {
entity: 'Task',
operations: {
get: {},
getAll: {},
create: {}
}
});
export default app;

View File

@ -0,0 +1,17 @@
{
"dependencies": {
"react": "^18.2.0",
"wasp": "file:.wasp/out/sdk/wasp"
},
"devDependencies": {
"@types/react": "^18.0.37",
"prisma": "5.19.1",
"typescript": "^5.1.0",
"vite": "^4.3.9",
"wasp-config": "file:../../../../../../../../.local/share/wasp-lang/0.15.0/data/packages/wasp-config"
},
"name": "waspComplexTest",
"react-redux": "^7.1.3",
"redux": "^4.0.5",
"type": "module"
}

View File

@ -0,0 +1,20 @@
datasource db {
provider = "postgresql"
// Wasp requires that the url is set to the DATABASE_URL environment variable.
url = env("DATABASE_URL")
}
// Wasp requires the `prisma-client-js` generator to be present.
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
}
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean @default(false)
}

View File

@ -0,0 +1,89 @@
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
}
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main p {
font-size: 1.2rem;
}
.logo {
margin-bottom: 2rem;
}
.logo img {
max-height: 200px;
}
.welcome-title {
font-weight: 500;
}
.welcome-subtitle {
font-weight: 400;
margin-bottom: 3rem;
}
.buttons {
display: flex;
flex-direction: row;
}
.buttons .button:not(:last-child) {
margin-right: 0.5rem;
}
.button {
border-radius: 3px;
font-size: 1.2rem;
padding: 1rem 2rem;
text-align: center;
font-weight: 700;
text-decoration: none;
}
.button-filled {
border: 2px solid #bf9900;
background-color: #bf9900;
color: #f4f4f4;
}
.button-outline {
border: 2px solid #8a9cff;
color: #8a9cff;
background-color: none;
}
code {
border-radius: 5px;
padding: 0.2rem;
background: #efefef;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}

View File

@ -0,0 +1,41 @@
import waspLogo from './waspLogo.png'
import './Main.css'
export const MainPage = () => {
return (
<div className="container">
<main>
<div className="logo">
<img src={waspLogo} alt="wasp" />
</div>
<h2 className="welcome-title">
Welcome to Wasp - you just started a new app!
</h2>
<h3 className="welcome-subtitle">
This is page <code>MainPage</code> located at route <code>/</code>.
Open <code>src/MainPage.jsx</code> to edit it.
</h3>
<div className="buttons">
<a
className="button button-filled"
href="https://wasp-lang.dev/docs/tutorial/create"
target="_blank"
rel="noreferrer noopener"
>
Take the Tutorial
</a>
<a
className="button button-outline"
href="https://discord.com/invite/rzdnErX"
target="_blank"
rel="noreferrer noopener"
>
Chat on Discord
</a>
</div>
</main>
</div>
)
}

View File

@ -0,0 +1,25 @@
import type {
OnAfterSignupHook,
OnBeforeOAuthRedirectHook,
OnBeforeSignupHook,
} from 'wasp/server/auth'
export const onBeforeSignup: OnBeforeSignupHook = async (args) => {
const count = await args.prisma.user.count()
console.log('before', count)
console.log(args.providerId)
}
export const onAfterSignup: OnAfterSignupHook = async (args) => {
const count = await args.prisma.user.count()
console.log('after', count)
console.log('user', args.user)
}
export const onBeforeOAuthRedirect: OnBeforeOAuthRedirectHook = async (
args,
) => {
console.log('redirect to', args.url.toString())
return { url: args.url }
}

View File

@ -0,0 +1,9 @@
export default function App({ children }) {
return (
<div className="app">
<h1>Root component</h1>
{children}
</div>
);
}

View File

@ -0,0 +1,4 @@
export default function myClientSetupFunction() {
// Do some client setup here.
}

View File

@ -0,0 +1,4 @@
export const foo = async (args) => {
return 1
}

View File

@ -0,0 +1,5 @@
import { MiddlewareConfigFn } from 'wasp/server'
export const fooBarNamespaceMiddlewareFn: MiddlewareConfigFn = (middlewareConfig) => {
return middlewareConfig
}

View File

@ -0,0 +1,13 @@
import { FooBar, FooBaz } from 'wasp/server/api'
import { MiddlewareConfigFn } from 'wasp/server'
export const fooBar: FooBar = (req, res, context) => {
res.set('Access-Control-Allow-Origin', '*')
res.json({ msg: 'Hello, context.user.username!' })
}
export const fooBaz: FooBaz = (req, res, context) => {
res.json({ msg: 'Hello, stranger!' })
}
export const fooBarMiddlewareFn: MiddlewareConfigFn = (middlewareConfig) => {
return middlewareConfig
}

View File

@ -0,0 +1,4 @@
export const foo = async (args) => {
return 1
}

View File

@ -0,0 +1,5 @@
import { ReturnHelloJob } from 'wasp/server/jobs'
export const returnHello: ReturnHelloJob<{ name: string }, string> = async (args) => {
return args.name
}

View File

@ -0,0 +1,4 @@
export default function mySetupFunction() {
// Do some server setup here.
}

View File

@ -0,0 +1,4 @@
export const foo = async (args) => {
return 1
}

View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.src.json" },
{ "path": "./tsconfig.wasp.json" }
]
}

View File

@ -0,0 +1,42 @@
// =============================== IMPORTANT =================================
//
// This file is only used for Wasp IDE support. You can change it to configure
// your IDE checks, but none of these options will affect the TypeScript
// compiler. Proper TS compiler configuration in Wasp is coming soon :)
{
"compilerOptions": {
"module": "esnext",
"target": "esnext",
// We're bundling all code in the end so this is the most appropriate option,
// it's also important for autocomplete to work properly.
"moduleResolution": "bundler",
// JSX support
"jsx": "preserve",
"strict": true,
// Allow default imports.
"esModuleInterop": true,
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"typeRoots": [
// This is needed to properly support Vitest testing with jest-dom matchers.
// Types for jest-dom are not recognized automatically and Typescript complains
// about missing types e.g. when using `toBeInTheDocument` and other matchers.
"node_modules/@testing-library",
// Specifying type roots overrides the default behavior of looking at the
// node_modules/@types folder so we had to list it explicitly.
// Source 1: https://www.typescriptlang.org/tsconfig#typeRoots
// Source 2: https://github.com/testing-library/jest-dom/issues/546#issuecomment-1889884843
"node_modules/@types"
],
// Since this TS config is used only for IDE support and not for
// compilation, the following directory doesn't exist. We need to specify
// it to prevent this error:
// https://stackoverflow.com/questions/42609768/typescript-error-cannot-write-file-because-it-would-overwrite-input-file
"outDir": ".wasp/phantom"
},
"include": ["src"]
}

View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"skipLibCheck": true,
"target": "ES2022",
"isolatedModules": true,
"moduleDetection": "force",
// linting
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"module": "NodeNext",
"noEmit": true,
"lib": ["ES2023"],
},
"include": ["main.wasp.ts"]
}

View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
export default defineConfig({
server: {
open: true,
},
})