mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
- add extra users to gen_seed
- add some test-ids - add new log in provider in dev only for testing - dont rate limit in dev - remove testids from production builds - add test npm scripts - user auth utils - setup script - example tests - playwright config
This commit is contained in:
+1
-1
@@ -73,8 +73,8 @@ trace/types.json
|
||||
/src/local
|
||||
|
||||
# Playwright
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
/tests/auth/*.json
|
||||
|
||||
@@ -60,6 +60,10 @@ run:
|
||||
.PHONY: init
|
||||
init: copy-env npm-install start run-migrations bootstrap-db bootstrap-metrics run
|
||||
|
||||
.PHONY: rerun
|
||||
rerun: start bootstrap-db
|
||||
npm run dev
|
||||
|
||||
.PHONY: init-devcontainer
|
||||
init-devcontainer: copy-env npm-install run-migrations bootstrap-db bootstrap-metrics
|
||||
|
||||
|
||||
+251
-240
@@ -1,11 +1,11 @@
|
||||
// @ts-check
|
||||
import { withAxiom } from "@civitai/next-axiom";
|
||||
import { withAxiom } from '@civitai/next-axiom';
|
||||
import bundlAnalyzer from '@next/bundle-analyzer';
|
||||
import packageJson from './package.json' assert { type: 'json' };
|
||||
import bundlAnalyzer from '@next/bundle-analyzer'
|
||||
|
||||
const withBundleAnalyzer = bundlAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* Don't be scared of the generics here.
|
||||
@@ -19,246 +19,257 @@ function defineNextConfig(config) {
|
||||
return withBundleAnalyzer(config);
|
||||
}
|
||||
|
||||
export default defineNextConfig(withAxiom({
|
||||
env: {
|
||||
version: packageJson.version,
|
||||
},
|
||||
reactStrictMode: true,
|
||||
productionBrowserSourceMaps: true,
|
||||
// Next.js i18n docs: https://nextjs.org/docs/advanced-features/i18n-routing
|
||||
i18n: {
|
||||
locales: ['en'],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
generateEtags: false,
|
||||
compress: false,
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{ hostname: 's3.us-west-1.wasabisys.com', },
|
||||
{ hostname: 'model-share.s3.us-west-1.wasabisys.com', },
|
||||
{ hostname: 'civitai-prod.s3.us-west-1.wasabisys.com', },
|
||||
{ hostname: 'civitai-dev.s3.us-west-1.wasabisys.com', },
|
||||
{ hostname: 'image.civitai.com', },
|
||||
]
|
||||
// domains: [
|
||||
// 's3.us-west-1.wasabisys.com',
|
||||
// 'model-share.s3.us-west-1.wasabisys.com',
|
||||
// 'civitai-prod.s3.us-west-1.wasabisys.com',
|
||||
// 'civitai-dev.s3.us-west-1.wasabisys.com',
|
||||
// 'image.civitai.com',
|
||||
// ],
|
||||
},
|
||||
transpilePackages: ['lodash', 'lodash-es', 'prisma'],
|
||||
experimental: {
|
||||
// scrollRestoration: true,
|
||||
largePageDataBytes: 512 * 100000,
|
||||
optimizePackageImports: [
|
||||
'@civitai/client',
|
||||
'./srs/libs/form'
|
||||
]
|
||||
},
|
||||
headers: async () => {
|
||||
// Add X-Robots-Tag header to all pages matching /sitemap.xml and /sitemap-models.xml /sitemap-articles.xml, etc
|
||||
const headers = [{
|
||||
source: '/sitemap(-\\w+)?.xml',
|
||||
headers: [
|
||||
{ key: 'X-Robots-Tag', value: 'noindex' },
|
||||
{ key: 'Content-Type', value: 'application/xml' },
|
||||
{ key: 'Cache-Control', value: 'public, max-age=86400, must-revalidate' }
|
||||
export default defineNextConfig(
|
||||
withAxiom({
|
||||
env: {
|
||||
version: packageJson.version,
|
||||
},
|
||||
reactStrictMode: true,
|
||||
productionBrowserSourceMaps: true,
|
||||
// Next.js i18n docs: https://nextjs.org/docs/advanced-features/i18n-routing
|
||||
i18n: {
|
||||
locales: ['en'],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
generateEtags: false,
|
||||
compress: false,
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{ hostname: 's3.us-west-1.wasabisys.com' },
|
||||
{ hostname: 'model-share.s3.us-west-1.wasabisys.com' },
|
||||
{ hostname: 'civitai-prod.s3.us-west-1.wasabisys.com' },
|
||||
{ hostname: 'civitai-dev.s3.us-west-1.wasabisys.com' },
|
||||
{ hostname: 'image.civitai.com' },
|
||||
],
|
||||
}];
|
||||
// domains: [
|
||||
// 's3.us-west-1.wasabisys.com',
|
||||
// 'model-share.s3.us-west-1.wasabisys.com',
|
||||
// 'civitai-prod.s3.us-west-1.wasabisys.com',
|
||||
// 'civitai-dev.s3.us-west-1.wasabisys.com',
|
||||
// 'image.civitai.com',
|
||||
// ],
|
||||
},
|
||||
compiler:
|
||||
process.env.NODE_ENV === 'production'
|
||||
? {
|
||||
reactRemoveProperties: { properties: ['^data-testid$'] },
|
||||
// removeConsole: true,
|
||||
}
|
||||
: {},
|
||||
transpilePackages: ['lodash', 'lodash-es', 'prisma'],
|
||||
experimental: {
|
||||
// scrollRestoration: true,
|
||||
largePageDataBytes: 512 * 100000,
|
||||
optimizePackageImports: ['@civitai/client', './srs/libs/form'],
|
||||
},
|
||||
headers: async () => {
|
||||
// Add X-Robots-Tag header to all pages matching /sitemap.xml and /sitemap-models.xml /sitemap-articles.xml, etc
|
||||
const headers = [
|
||||
{
|
||||
source: '/sitemap(-\\w+)?.xml',
|
||||
headers: [
|
||||
{ key: 'X-Robots-Tag', value: 'noindex' },
|
||||
{ key: 'Content-Type', value: 'application/xml' },
|
||||
{ key: 'Cache-Control', value: 'public, max-age=86400, must-revalidate' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
headers.push({
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'X-Robots-Tag',
|
||||
value: 'noindex',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
headers.push({
|
||||
source: '/:path*',
|
||||
headers: [{
|
||||
key: 'X-Robots-Tag',
|
||||
value: 'noindex',
|
||||
}],
|
||||
headers: [{ key: 'X-Frame-Options', value: 'DENY' }],
|
||||
});
|
||||
}
|
||||
|
||||
headers.push({
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{ key: 'X-Frame-Options', value: 'DENY' },
|
||||
],
|
||||
})
|
||||
|
||||
return headers;
|
||||
},
|
||||
poweredByHeader: false,
|
||||
redirects: async () => {
|
||||
return [
|
||||
{
|
||||
source: '/api/download/training-data/:modelVersionId',
|
||||
destination: '/api/download/models/:modelVersionId?type=Training%20Data',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/github/:path*',
|
||||
destination: 'https://github.com/civitai/civitai/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/discord',
|
||||
destination: 'https://discord.gg/civitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/twitter',
|
||||
destination: 'https://twitter.com/HelloCivitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/reddit',
|
||||
destination: 'https://reddit.com/r/civitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/instagram',
|
||||
destination: 'https://www.instagram.com/hellocivitai/',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/tiktok',
|
||||
destination: 'https://www.tiktok.com/@hellocivitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/youtube',
|
||||
destination: 'https://www.youtube.com/@civitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/twitch',
|
||||
destination: 'https://www.twitch.tv/civitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/ideas',
|
||||
destination: 'https://github.com/civitai/civitai/discussions/categories/ideas',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/v/civitai-link-intro',
|
||||
destination: 'https://youtu.be/EHUjiDgh-MI',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/v/civitai-link-installation',
|
||||
destination: 'https://youtu.be/fs-Zs-fvxb0',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/gallery/:path*',
|
||||
destination: '/images/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/appeal',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-5844/5NXSA2EIT3YOS2JSF7',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/canny/feedback',
|
||||
destination: 'https://feedback.civitai.com/?b=feature-request',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/feedback',
|
||||
destination: 'https://feedback.civitai.com/?b=feature-request',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/canny/bugs',
|
||||
destination: 'https://civitai-team.myfreshworks.com/login/auth/civitai?client_id=451979510707337272&redirect_uri=https%3A%2F%2Fcivitai.freshdesk.com%2Ffreshid%2Fcustomer_authorize_callback%3Fhd%3Dsupport.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/bugs',
|
||||
destination: 'https://civitai-team.myfreshworks.com/login/auth/civitai?client_id=451979510707337272&redirect_uri=https%3A%2F%2Fcivitai.freshdesk.com%2Ffreshid%2Fcustomer_authorize_callback%3Fhd%3Dsupport.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/support-portal',
|
||||
destination: 'https://civitai-team.myfreshworks.com/login/auth/civitai?client_id=451979510707337272&redirect_uri=https%3A%2F%2Fcivitai.freshdesk.com%2Ffreshid%2Fcustomer_authorize_callback%3Fhd%3Dsupport.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/leaderboard',
|
||||
destination: '/leaderboard/overall',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/forms/bounty-refund',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-8331/R30FGV9JFHLF527GGN',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/forms/press-inquiry',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-9351/RZXWRNLV9Q1D32ACNP',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/forms/matching-partner',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-9431/IQOAS1RXWHI1E2I1S3',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/holiday2023',
|
||||
destination: '/events/holiday2023',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/blocked-by-octoml',
|
||||
destination: '/articles/3307',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/blocked-by-provider',
|
||||
destination: '/articles/3307',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/wiki',
|
||||
destination: 'https://wiki.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/education',
|
||||
destination: 'https://education.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/advertise-with-us',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-10211/MIN35AIDXBZ7BTD5MG',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/ad-feedback',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-9711/WIMNO6V738T4ZBTPXP',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/cosmetic-shop',
|
||||
destination: '/shop',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/shop/cosmetic-shop',
|
||||
destination: '/shop',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/projectodyssey_season2',
|
||||
destination: '/collections/6503138',
|
||||
permanent: true,
|
||||
}
|
||||
];
|
||||
},
|
||||
output: 'standalone',
|
||||
}));
|
||||
return headers;
|
||||
},
|
||||
poweredByHeader: false,
|
||||
redirects: async () => {
|
||||
return [
|
||||
{
|
||||
source: '/api/download/training-data/:modelVersionId',
|
||||
destination: '/api/download/models/:modelVersionId?type=Training%20Data',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/github/:path*',
|
||||
destination: 'https://github.com/civitai/civitai/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/discord',
|
||||
destination: 'https://discord.gg/civitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/twitter',
|
||||
destination: 'https://twitter.com/HelloCivitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/reddit',
|
||||
destination: 'https://reddit.com/r/civitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/instagram',
|
||||
destination: 'https://www.instagram.com/hellocivitai/',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/tiktok',
|
||||
destination: 'https://www.tiktok.com/@hellocivitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/youtube',
|
||||
destination: 'https://www.youtube.com/@civitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/twitch',
|
||||
destination: 'https://www.twitch.tv/civitai',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/ideas',
|
||||
destination: 'https://github.com/civitai/civitai/discussions/categories/ideas',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/v/civitai-link-intro',
|
||||
destination: 'https://youtu.be/EHUjiDgh-MI',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/v/civitai-link-installation',
|
||||
destination: 'https://youtu.be/fs-Zs-fvxb0',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/gallery/:path*',
|
||||
destination: '/images/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/appeal',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-5844/5NXSA2EIT3YOS2JSF7',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/canny/feedback',
|
||||
destination: 'https://feedback.civitai.com/?b=feature-request',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/feedback',
|
||||
destination: 'https://feedback.civitai.com/?b=feature-request',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/canny/bugs',
|
||||
destination:
|
||||
'https://civitai-team.myfreshworks.com/login/auth/civitai?client_id=451979510707337272&redirect_uri=https%3A%2F%2Fcivitai.freshdesk.com%2Ffreshid%2Fcustomer_authorize_callback%3Fhd%3Dsupport.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/bugs',
|
||||
destination:
|
||||
'https://civitai-team.myfreshworks.com/login/auth/civitai?client_id=451979510707337272&redirect_uri=https%3A%2F%2Fcivitai.freshdesk.com%2Ffreshid%2Fcustomer_authorize_callback%3Fhd%3Dsupport.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/support-portal',
|
||||
destination:
|
||||
'https://civitai-team.myfreshworks.com/login/auth/civitai?client_id=451979510707337272&redirect_uri=https%3A%2F%2Fcivitai.freshdesk.com%2Ffreshid%2Fcustomer_authorize_callback%3Fhd%3Dsupport.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/leaderboard',
|
||||
destination: '/leaderboard/overall',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/forms/bounty-refund',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-8331/R30FGV9JFHLF527GGN',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/forms/press-inquiry',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-9351/RZXWRNLV9Q1D32ACNP',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/forms/matching-partner',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-9431/IQOAS1RXWHI1E2I1S3',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/holiday2023',
|
||||
destination: '/events/holiday2023',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/blocked-by-octoml',
|
||||
destination: '/articles/3307',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/blocked-by-provider',
|
||||
destination: '/articles/3307',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/wiki',
|
||||
destination: 'https://wiki.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/education',
|
||||
destination: 'https://education.civitai.com',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/advertise-with-us',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-10211/MIN35AIDXBZ7BTD5MG',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/ad-feedback',
|
||||
destination: 'https://forms.clickup.com/8459928/f/825mr-9711/WIMNO6V738T4ZBTPXP',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/cosmetic-shop',
|
||||
destination: '/shop',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/shop/cosmetic-shop',
|
||||
destination: '/shop',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/projectodyssey_season2',
|
||||
destination: '/collections/6503138',
|
||||
permanent: true,
|
||||
},
|
||||
];
|
||||
},
|
||||
output: 'standalone',
|
||||
})
|
||||
);
|
||||
|
||||
+3
-3
@@ -40,9 +40,9 @@
|
||||
"analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build",
|
||||
"tsc:trace": "tsc --generateTrace trace --incremental false",
|
||||
"tsc:analyze": "npx analyze-trace trace",
|
||||
"test": "npx playwright test",
|
||||
"test:ui": "npx playwright test --ui",
|
||||
"test:gen": "npx playwright codegen"
|
||||
"test": "cross-env NODE_ENV=development npx playwright test",
|
||||
"test:ui": "cross-env NODE_ENV=development npx playwright test --ui",
|
||||
"test:gen": "cross-env NODE_ENV=development npx playwright codegen"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// if (process.env.NODE_ENV === 'development') {
|
||||
// dotenv.config({
|
||||
// path: ['.env.development.local', '.env.local', '.env.development', '.env'],
|
||||
// override: false,
|
||||
// });
|
||||
// }
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: './tests',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
// workers: 1,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
// reporter: 'html',
|
||||
reporter: process.env.CI ? 'html' : 'line',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: 'http://localhost:3000',
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
// Setup project
|
||||
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
|
||||
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
dependencies: ['setup'],
|
||||
},
|
||||
{
|
||||
name: 'firefox',
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
dependencies: ['setup'],
|
||||
},
|
||||
// {
|
||||
// name: 'webkit',
|
||||
// use: { ...devices['Desktop Safari'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
{
|
||||
name: 'Mobile Safari',
|
||||
use: { ...devices['iPhone 12'] },
|
||||
dependencies: ['setup'],
|
||||
},
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: {
|
||||
command: 'npm run dev',
|
||||
url: 'http://localhost:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
});
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
import { checkLocalDb, insertRows } from './utils';
|
||||
// import { fetchBlob } from '~/utils/file-utils';
|
||||
|
||||
const numRows = 1000;
|
||||
const numRows = 200;
|
||||
|
||||
faker.seed(1337);
|
||||
const randw = faker.helpers.weightedArrayElement;
|
||||
@@ -157,11 +157,11 @@ const truncateNotificationRows = async () => {
|
||||
const genUsers = (num: number, includeCiv = false) => {
|
||||
const ret = [];
|
||||
|
||||
if (includeCiv) {
|
||||
num -= 1;
|
||||
const extraUsers = [];
|
||||
|
||||
if (includeCiv) {
|
||||
// civ user
|
||||
const civUser = [
|
||||
extraUsers.push([
|
||||
'Civitai',
|
||||
'hello@civitai.com',
|
||||
null,
|
||||
@@ -194,15 +194,237 @@ const genUsers = (num: number, includeCiv = false) => {
|
||||
null,
|
||||
'Eligible',
|
||||
null,
|
||||
];
|
||||
]);
|
||||
|
||||
ret.push(civUser);
|
||||
// - test users
|
||||
|
||||
// mod
|
||||
extraUsers.push([
|
||||
'Test - Moderator', // name
|
||||
'test-mod@civitai.com', // email
|
||||
null,
|
||||
null,
|
||||
1, // id
|
||||
false, // blurnsfw
|
||||
true, // shownsfw
|
||||
'test-mod', // username
|
||||
true, // isMod
|
||||
false,
|
||||
'2022-11-13 00:00:00.000',
|
||||
null, // deletedAt
|
||||
null, // bannedAt
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
'{"fp": "fp16", "size": "pruned", "format": "SafeTensor"}',
|
||||
null,
|
||||
'{Moderation,Buzz}', // onboardingSteps
|
||||
null,
|
||||
'{}', // meta
|
||||
'{}', // settings
|
||||
null, // "mutedAt"
|
||||
false, // muted
|
||||
31, // "browsingLevel"
|
||||
15, // onboarding
|
||||
'{}', // "publicSettings"
|
||||
null, // "muteConfirmedAt"
|
||||
false,
|
||||
null,
|
||||
'Eligible',
|
||||
`ctm_01j6${faker.string.alphanumeric(22)}`,
|
||||
]);
|
||||
|
||||
// newbie
|
||||
extraUsers.push([
|
||||
'Test - Newbie', // name
|
||||
'test-newbie@civitai.com', // email
|
||||
null,
|
||||
null,
|
||||
2, // id
|
||||
true, // blurnsfw
|
||||
false, // shownsfw
|
||||
'test-newbie', // username
|
||||
false, // isMod
|
||||
false,
|
||||
'2024-11-13 00:00:00.000',
|
||||
null, // deletedAt
|
||||
null, // bannedAt
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
'{"fp": "fp16", "size": "pruned", "format": "SafeTensor"}',
|
||||
null,
|
||||
'{Moderation,Buzz}',
|
||||
null,
|
||||
'{}', // meta
|
||||
'{}', // settings
|
||||
null, // "mutedAt"
|
||||
false, // muted
|
||||
1, // "browsingLevel"
|
||||
0, // onboarding
|
||||
'{}', // "publicSettings"
|
||||
null, // "muteConfirmedAt"
|
||||
false,
|
||||
null,
|
||||
'Eligible',
|
||||
null,
|
||||
]);
|
||||
|
||||
// degen
|
||||
extraUsers.push([
|
||||
'Test - Degen', // name
|
||||
'test-degen@civitai.com', // email
|
||||
'2023-11-14 00:00:00.000',
|
||||
null,
|
||||
3, // id
|
||||
false, // blurnsfw
|
||||
true, // shownsfw
|
||||
'test-degen', // username
|
||||
false, // isMod
|
||||
false,
|
||||
'2023-11-13 00:00:00.000',
|
||||
null, // deletedAt
|
||||
null, // bannedAt
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
'{"fp": "fp16", "size": "pruned", "format": "SafeTensor"}',
|
||||
null,
|
||||
'{}',
|
||||
null,
|
||||
'{"scores": {"total": 374, "users": 300, "images": 70, "models": 4, "reportsActioned": 50}}', // meta
|
||||
'{}', // settings
|
||||
null, // "mutedAt"
|
||||
false, // muted
|
||||
31, // "browsingLevel"
|
||||
15, // onboarding
|
||||
'{}', // "publicSettings"
|
||||
null, // "muteConfirmedAt"
|
||||
false,
|
||||
null,
|
||||
'Eligible',
|
||||
`ctm_01j6${faker.string.alphanumeric(22)}`,
|
||||
]);
|
||||
|
||||
// banned
|
||||
extraUsers.push([
|
||||
'Test - Banned', // name
|
||||
'test-banned@civitai.com', // email
|
||||
'2023-11-14 00:00:00.000',
|
||||
null,
|
||||
4, // id
|
||||
false, // blurnsfw
|
||||
true, // shownsfw
|
||||
'test-banned', // username
|
||||
false, // isMod
|
||||
false,
|
||||
'2023-11-13 00:00:00.000',
|
||||
null, // deletedAt
|
||||
'2023-11-17 00:00:00.000', // bannedAt
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
'{"fp": "fp16", "size": "pruned", "format": "SafeTensor"}',
|
||||
null,
|
||||
'{}',
|
||||
null,
|
||||
'{}', // meta
|
||||
'{}', // settings
|
||||
null, // "mutedAt"
|
||||
false, // muted
|
||||
31, // "browsingLevel"
|
||||
15, // onboarding
|
||||
'{}', // "publicSettings"
|
||||
null, // "muteConfirmedAt"
|
||||
false,
|
||||
null,
|
||||
'Eligible',
|
||||
`ctm_01j6${faker.string.alphanumeric(22)}`,
|
||||
]);
|
||||
|
||||
// deleted
|
||||
extraUsers.push([
|
||||
'Test - Deleted', // name
|
||||
'test-deleted@civitai.com', // email
|
||||
'2023-11-14 00:00:00.000',
|
||||
null,
|
||||
5, // id
|
||||
false, // blurnsfw
|
||||
true, // shownsfw
|
||||
'test-deleted', // username
|
||||
false, // isMod
|
||||
false,
|
||||
'2023-11-13 00:00:00.000',
|
||||
'2023-11-17 00:00:00.000', // deletedAt
|
||||
null, // bannedAt
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
'{"fp": "fp16", "size": "pruned", "format": "SafeTensor"}',
|
||||
null,
|
||||
'{}',
|
||||
null,
|
||||
'{}', // meta
|
||||
'{}', // settings
|
||||
null, // "mutedAt"
|
||||
false, // muted
|
||||
31, // "browsingLevel"
|
||||
15, // onboarding
|
||||
'{}', // "publicSettings"
|
||||
null, // "muteConfirmedAt"
|
||||
false,
|
||||
null,
|
||||
'Eligible',
|
||||
`ctm_01j6${faker.string.alphanumeric(22)}`,
|
||||
]);
|
||||
|
||||
// muted
|
||||
extraUsers.push([
|
||||
'Test - Muted', // name
|
||||
'test-muted@civitai.com', // email
|
||||
'2023-11-14 00:00:00.000',
|
||||
null,
|
||||
6, // id
|
||||
false, // blurnsfw
|
||||
true, // shownsfw
|
||||
'test-muted', // username
|
||||
false, // isMod
|
||||
false,
|
||||
'2023-11-13 00:00:00.000',
|
||||
null, // deletedAt
|
||||
null, // bannedAt
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
'{"fp": "fp16", "size": "pruned", "format": "SafeTensor"}',
|
||||
null,
|
||||
'{}',
|
||||
null,
|
||||
'{}', // meta
|
||||
'{}', // settings
|
||||
'2023-11-17 00:00:00.000', // "mutedAt"
|
||||
true, // muted
|
||||
31, // "browsingLevel"
|
||||
15, // onboarding
|
||||
'{}', // "publicSettings"
|
||||
'2023-11-17 01:00:00.000', // "muteConfirmedAt"
|
||||
false,
|
||||
null,
|
||||
'Eligible',
|
||||
`ctm_01j6${faker.string.alphanumeric(22)}`,
|
||||
]);
|
||||
|
||||
// subscriber
|
||||
// customerSubscription?
|
||||
|
||||
ret.push(...extraUsers);
|
||||
num += extraUsers.length;
|
||||
}
|
||||
|
||||
const seenUserNames: string[] = [];
|
||||
|
||||
// random users
|
||||
for (let step = 1; step <= num; step++) {
|
||||
for (let step = extraUsers.length + 1; step <= num; step++) {
|
||||
const created = faker.date.past({ years: 3 }).toISOString();
|
||||
const isMuted = fbool(0.01);
|
||||
let username = faker.internet.userName();
|
||||
|
||||
@@ -41,6 +41,7 @@ export function ChatButton() {
|
||||
<ActionIcon
|
||||
variant={state.open ? 'filled' : undefined}
|
||||
onClick={() => setState((prev) => ({ ...prev, open: !state.open }))}
|
||||
data-testid="open-chat"
|
||||
>
|
||||
<IconMessage2 />
|
||||
</ActionIcon>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { Stack, Group, Button, Alert, Center, createStyles } from '@mantine/core';
|
||||
import { Form, InputRTE, useForm } from '~/libs/form';
|
||||
import { useRef, useState, useMemo } from 'react';
|
||||
import { UpsertCommentV2Input, upsertCommentv2Schema } from '~/server/schema/commentv2.schema';
|
||||
import { showErrorNotification } from '~/utils/notifications';
|
||||
import { trpc } from '~/utils/trpc';
|
||||
import produce from 'immer';
|
||||
import type { EditorCommandsRef } from '~/components/RichTextEditor/RichTextEditorComponent';
|
||||
import { SimpleUser } from '~/server/selectors/user.selector';
|
||||
import { Alert, Button, Center, createStyles, Group, Stack } from '@mantine/core';
|
||||
import { IconLock } from '@tabler/icons-react';
|
||||
import produce from 'immer';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
useCommentsContext,
|
||||
useNewCommentStore,
|
||||
useRootThreadContext,
|
||||
} from '~/components/CommentsV2/CommentsProvider';
|
||||
import type { EditorCommandsRef } from '~/components/RichTextEditor/RichTextEditorComponent';
|
||||
import { Form, InputRTE, useForm } from '~/libs/form';
|
||||
import { UpsertCommentV2Input, upsertCommentv2Schema } from '~/server/schema/commentv2.schema';
|
||||
import { SimpleUser } from '~/server/selectors/user.selector';
|
||||
import { removeDuplicates } from '~/utils/array-helpers';
|
||||
import { showErrorNotification } from '~/utils/notifications';
|
||||
import { trpc } from '~/utils/trpc';
|
||||
|
||||
/*
|
||||
Most use cases of this form will require cancel/submit buttons to be displayed
|
||||
@@ -173,6 +173,7 @@ export const CommentForm = ({
|
||||
root: borderless ? 'border-none' : undefined,
|
||||
content: cx(classes.content, 'rounded-3xl'),
|
||||
}}
|
||||
data-testid="comment-form"
|
||||
/>
|
||||
{focused && (
|
||||
<Group position="right">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Center, Loader, createStyles, Stack, Alert, Text } from '@mantine/core';
|
||||
import { Alert, Center, createStyles, Loader, Stack, Text } from '@mantine/core';
|
||||
import { IconInbox } from '@tabler/icons-react';
|
||||
import { GeneratedImage } from '~/components/ImageGeneration/GeneratedImage';
|
||||
import { useGetTextToImageRequestsImages } from '~/components/ImageGeneration/utils/generationRequestHooks';
|
||||
import { InViewLoader } from '~/components/InView/InViewLoader';
|
||||
import { useFiltersContext } from '~/providers/FiltersProvider';
|
||||
import { generationPanel } from '~/store/generation.store';
|
||||
import { isDefined } from '~/utils/type-guards';
|
||||
import { useFiltersContext } from '~/providers/FiltersProvider';
|
||||
|
||||
export function Feed() {
|
||||
const { classes } = useStyles();
|
||||
@@ -68,7 +68,7 @@ export function Feed() {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 px-3">
|
||||
{/* <GeneratedImagesBuzzPrompt /> */}
|
||||
<div className={classes.grid}>
|
||||
<div className={classes.grid} data-testid="generation-feed-list">
|
||||
{steps.map((step) =>
|
||||
step.images
|
||||
.filter((x) => x.status === 'succeeded')
|
||||
|
||||
@@ -241,6 +241,33 @@ export function createAuthOptions(req?: AuthedRequest): NextAuthOptions {
|
||||
}
|
||||
},
|
||||
}),
|
||||
...(isDev
|
||||
? [
|
||||
CredentialsProvider({
|
||||
id: 'testing-login',
|
||||
name: 'Testing Login',
|
||||
credentials: {
|
||||
id: { label: 'id', type: 'text' },
|
||||
},
|
||||
async authorize(credentials) {
|
||||
if (!isDev) return null;
|
||||
|
||||
const { id } = credentials ?? {};
|
||||
if (!id) throw new Error('No id provided.');
|
||||
|
||||
try {
|
||||
const userId = Number(id);
|
||||
const user = await getSessionUser({ userId });
|
||||
if (!user) throw new Error('No user found.');
|
||||
return user;
|
||||
} catch (e: unknown) {
|
||||
const err = e as Error;
|
||||
throw new Error(`Failed to authenticate: ${err.message}.`);
|
||||
}
|
||||
},
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
],
|
||||
cookies: {
|
||||
sessionToken: {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Container, Loader, Stack } from '@mantine/core';
|
||||
import { signIn } from 'next-auth/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { NotFound } from '~/components/AppLayout/NotFound';
|
||||
import { isDev } from '~/env/other';
|
||||
|
||||
export default function DevLoginPage() {
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDev) return;
|
||||
|
||||
const { userId } = router.query;
|
||||
|
||||
if (userId) {
|
||||
console.log('Logging in as user', userId);
|
||||
setLoading(true);
|
||||
signIn('testing-login', { id: userId as string, callbackUrl: '/' })
|
||||
.then(() => setLoading(false))
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [router.query]);
|
||||
|
||||
if (!isDev) return <NotFound />;
|
||||
|
||||
return (
|
||||
<Container size="xs">
|
||||
<Stack>
|
||||
<h1>Development Login</h1>
|
||||
<p>
|
||||
This page is only available in development mode. Pass the user ID in the query string to
|
||||
log in as that user.
|
||||
</p>
|
||||
<p>
|
||||
Example: <code>?userId=1</code>
|
||||
</p>
|
||||
{loading && <Loader />}
|
||||
</Stack>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { isProd } from '~/env/other';
|
||||
import { isDev, isProd } from '~/env/other';
|
||||
import { purgeCache } from '~/server/cloudflare/client';
|
||||
import { CacheTTL } from '~/server/common/constants';
|
||||
import { logToAxiom } from '~/server/logging/client';
|
||||
@@ -106,7 +106,7 @@ export function rateLimit(rateLimits: undefined | RateLimit | RateLimit[]) {
|
||||
|
||||
return middleware(async ({ ctx, next, path }) => {
|
||||
// Skip if user is a moderator
|
||||
if (ctx.user?.isModerator) return await next();
|
||||
if (ctx.user?.isModerator || isDev) return await next();
|
||||
|
||||
// Get valid limits
|
||||
let validLimits: RateLimit[] = [];
|
||||
|
||||
@@ -148,3 +148,6 @@ export type BuzzWithdrawalGetPaginatedItem =
|
||||
|
||||
type ToolRouter = RouterOutput['tool'];
|
||||
export type ToolGetAllModel = ToolRouter['getAll']['items'][number];
|
||||
|
||||
type OrchestratorRouter = RouterOutput['orchestrator'];
|
||||
export type QueryGeneratedImages = OrchestratorRouter['queryGeneratedImages'];
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { expect, Page, test as setup } from '@playwright/test';
|
||||
import fs from 'fs';
|
||||
import { env } from '../src/env/server';
|
||||
import { testAuthData } from './auth/data';
|
||||
|
||||
// make sure we're using local DB for testing
|
||||
setup.beforeAll('check db', async () => {
|
||||
expect(
|
||||
env.DATABASE_URL.includes('localhost:15432') || env.DATABASE_URL.includes('db:5432')
|
||||
// ).toBeFalsy();
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
// save various user info for testing
|
||||
const authSetup = async (page: Page, d: (typeof testAuthData)[keyof typeof testAuthData]) => {
|
||||
if (fs.existsSync(d.path)) {
|
||||
// console.log(`Skipping auth setup for ${d.userId}, file exists: ${d.path}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Setting up user ID: ${d.userId}`);
|
||||
|
||||
await page.goto(`/testing/testing-login?userId=${d.userId}`);
|
||||
await page.waitForURL('/');
|
||||
// await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
await page.context().storageState({ path: d.path });
|
||||
};
|
||||
|
||||
setup('auth as mod', async ({ page }) => {
|
||||
await authSetup(page, testAuthData.mod);
|
||||
});
|
||||
setup('auth as newbie', async ({ page }) => {
|
||||
await authSetup(page, testAuthData.newbie);
|
||||
});
|
||||
setup('auth as degen', async ({ page }) => {
|
||||
await authSetup(page, testAuthData.degen);
|
||||
});
|
||||
setup('auth as banned', async ({ page }) => {
|
||||
await authSetup(page, testAuthData.banned);
|
||||
});
|
||||
// setup('auth as deleted', async ({ page }) => {
|
||||
// await authSetup(page, testAuthData.deleted);
|
||||
// });
|
||||
setup('auth as muted', async ({ page }) => {
|
||||
await authSetup(page, testAuthData.muted);
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
const basePath = 'tests/auth';
|
||||
|
||||
export const testAuthData = {
|
||||
mod: {
|
||||
userId: 1,
|
||||
path: `${basePath}/mod.json`,
|
||||
},
|
||||
newbie: {
|
||||
userId: 2,
|
||||
path: `${basePath}/newbie.json`,
|
||||
},
|
||||
degen: {
|
||||
userId: 3,
|
||||
path: `${basePath}/degen.json`,
|
||||
},
|
||||
banned: {
|
||||
userId: 4,
|
||||
path: `${basePath}/banned.json`,
|
||||
},
|
||||
deleted: {
|
||||
userId: 5,
|
||||
path: `${basePath}/deleted.json`,
|
||||
},
|
||||
muted: {
|
||||
userId: 6,
|
||||
path: `${basePath}/muted.json`,
|
||||
},
|
||||
};
|
||||
|
||||
export const authEmpty = { storageState: { cookies: [], origins: [] } };
|
||||
export const authMod = { storageState: testAuthData.mod.path };
|
||||
export const authNewbie = { storageState: testAuthData.newbie.path };
|
||||
export const authDegen = { storageState: testAuthData.degen.path };
|
||||
export const authBanned = { storageState: testAuthData.banned.path };
|
||||
// export const authDeleted = { storageState: testAuthData.deleted.path };
|
||||
export const authMuted = { storageState: testAuthData.muted.path };
|
||||
@@ -0,0 +1,195 @@
|
||||
import { expect, Locator, Page, test } from '@playwright/test';
|
||||
import { authDegen, authMod } from './auth/data';
|
||||
import { queryGeneratedImagesReturn } from './responses/queryGeneratedImages';
|
||||
import { apiResp } from './utils';
|
||||
|
||||
test('404', async ({ page }) => {
|
||||
// test 404 page
|
||||
await page.goto('/asdf');
|
||||
await expect(page.getByText('page could not be found')).toBeVisible();
|
||||
});
|
||||
|
||||
test('no login', async ({ page }) => {
|
||||
// test redirect on no login
|
||||
await page.goto('/user/account');
|
||||
await expect(page).toHaveURL('/');
|
||||
});
|
||||
|
||||
test.describe('examples', () => {
|
||||
test.use(authMod);
|
||||
|
||||
test('validate mod user', async ({ page }) => {
|
||||
await page.goto('/user/account');
|
||||
await expect(page.getByRole('textbox', { name: 'Name' })).toHaveValue('test-mod');
|
||||
});
|
||||
|
||||
test('chat button', async ({ page, isMobile }) => {
|
||||
// picking a low impact page
|
||||
await page.goto('/content/privacy');
|
||||
// note - this has exposed an issue, if you click the button before "something" happens, the chat window does not show up sometimes
|
||||
await page.waitForResponse(
|
||||
(resp) => resp.url().includes('/api/trpc/chat.getUnreadCount') && resp.status() === 200
|
||||
);
|
||||
// there are two buttons due to media queries
|
||||
const btn = page.getByTestId('open-chat').locator('visible=true');
|
||||
await btn.click();
|
||||
|
||||
// for localstorage changes, we could inspect or manually set the storage here
|
||||
const confirmBtn = page.getByRole('button', { name: 'Got it' });
|
||||
if (await confirmBtn.isVisible()) {
|
||||
await confirmBtn.click();
|
||||
}
|
||||
|
||||
// mobile view differs
|
||||
const locator = isMobile
|
||||
? page.getByText('Chats', { exact: true })
|
||||
: page.getByText('New Chat');
|
||||
|
||||
// toggle chat window and check visibility
|
||||
await expect(locator).toBeVisible();
|
||||
await btn.click();
|
||||
await expect(locator).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('comments', () => {
|
||||
test.use(authDegen);
|
||||
|
||||
test('comment chain', async ({ page }) => {
|
||||
const text =
|
||||
'testing long comment replies. this is a test of someone saying something, and then eventually responding to it.';
|
||||
|
||||
// Helper function to post a comment or reply
|
||||
const postComment = async (loc: Locator | Page, isReply: boolean) => {
|
||||
if (isReply) {
|
||||
await loc.getByRole('button', { name: 'Reply' }).click();
|
||||
} else {
|
||||
await loc.getByTestId('comment-form').click();
|
||||
}
|
||||
// TODO there is a delay here, sometimes cutting off the first letter
|
||||
// await page.keyboard.type(text);
|
||||
await loc.getByTestId('comment-form').locator('div').nth(2).fill(text);
|
||||
|
||||
// avoid race condition for clicking and awaiting response
|
||||
const [response] = await Promise.all([
|
||||
page.waitForResponse(
|
||||
(resp) => resp.url().includes('/api/trpc/commentv2.upsert') && resp.status() === 200
|
||||
),
|
||||
loc.getByRole('button', { name: 'Comment' }).click(),
|
||||
]);
|
||||
|
||||
// get the id in the response, and check the value in the relevant field
|
||||
const json = await response.json();
|
||||
const commentId = json?.result?.data?.json?.id;
|
||||
expect(commentId).toBeGreaterThan(0);
|
||||
const newLoc = page.locator(`#comment-${commentId}`);
|
||||
await expect(newLoc.getByRole('paragraph')).toHaveText(text);
|
||||
return newLoc;
|
||||
};
|
||||
|
||||
await page.goto('/images/1');
|
||||
|
||||
// Post initial comment
|
||||
const commentLoc = await postComment(page, false);
|
||||
|
||||
// replies
|
||||
const commentLoc2 = await postComment(commentLoc, true);
|
||||
const commentLoc3 = await postComment(commentLoc2, true);
|
||||
const commentLoc4 = await postComment(commentLoc3, true);
|
||||
const commentLoc5 = await postComment(commentLoc4, true);
|
||||
|
||||
await expect(commentLoc5).toBeInViewport();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('generation', () => {
|
||||
test.use(authDegen);
|
||||
|
||||
test('mock generation', async ({ page }) => {
|
||||
// override orchestrator calls with custom response
|
||||
await page.route(/\/api\/trpc\/orchestrator.queryGeneratedImages(\?|$)/, async (route) => {
|
||||
await route.fulfill({ json: queryGeneratedImagesReturn });
|
||||
});
|
||||
|
||||
await page.goto('/articles/1');
|
||||
await page.getByRole('button').filter({ hasText: 'Create' }).click();
|
||||
|
||||
const confirmBtn = page.getByRole('button', { name: 'I Confirm, Start Generating' });
|
||||
if (await confirmBtn.isVisible()) {
|
||||
await confirmBtn.click();
|
||||
}
|
||||
|
||||
// this is the queue button, but i can't seem to add a data-testid without the whole thing breaking
|
||||
await page.locator('div:nth-child(4) > .__mantine-ref-label').first().click();
|
||||
|
||||
await expect(page.getByTestId('generation-feed-list').locator('> div')).toHaveCount(19);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('error handling', () => {
|
||||
test.use(authDegen);
|
||||
|
||||
test('error uploading model', async ({ page }) => {
|
||||
await page.route('/api/trpc/tag.getAll*', async (route) => {
|
||||
await route.fulfill({
|
||||
json: apiResp({
|
||||
items: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'stuff',
|
||||
isCategory: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto('/models/create');
|
||||
|
||||
// test invalid form
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await expect(page.locator('form')).toContainText('Required');
|
||||
await expect(page.getByText('Cannot be empty', { exact: true })).toBeVisible();
|
||||
|
||||
await page.getByRole('textbox', { name: 'Name' }).fill('xyz');
|
||||
await page.getByText('Trained', { exact: true }).click();
|
||||
await page.getByRole('searchbox', { name: 'Category' }).click();
|
||||
await page.getByRole('option', { name: 'Stuff' }).click();
|
||||
await page.locator('.ProseMirror').fill('qwd');
|
||||
await page.getByRole('radio', { name: 'No' }).check();
|
||||
await page.getByRole('checkbox', { name: 'I acknowledge that I have' }).check();
|
||||
|
||||
await page.route('/api/trpc/model.upsert', async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
error: {
|
||||
json: {
|
||||
message: 'bad stuff.',
|
||||
code: -32600,
|
||||
data: {
|
||||
code: 'BAD_REQUEST',
|
||||
httpStatus: 400,
|
||||
path: 'model.upsert',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await expect(page.getByText('Failed to save model')).toBeVisible();
|
||||
|
||||
await page.unroute('/api/trpc/model.upsert');
|
||||
|
||||
await Promise.all([
|
||||
page.waitForResponse(
|
||||
(resp) => resp.url().includes('/api/trpc/model.upsert') && resp.status() === 200
|
||||
),
|
||||
page.getByRole('button', { name: 'Next' }).click(),
|
||||
]);
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Add version' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
export const apiResp = (d: any, meta?: any) => {
|
||||
return {
|
||||
result: {
|
||||
data: {
|
||||
json: d,
|
||||
meta: meta ?? {},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user