Skip to content
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

Add more e2e tests #106

Merged
merged 14 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ module.exports = {

```html
<!-- this example uses pure html for demonstration purposes -->
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand All @@ -162,7 +162,7 @@ You do this by adding a class of the theme's name to whatever you want themed. S

```html
<!-- this example uses pure html for demonstration purposes -->
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand Down Expand Up @@ -237,7 +237,7 @@ require('tailwindcss-themer')({
```

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand All @@ -250,7 +250,7 @@ require('tailwindcss-themer')({
```

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand All @@ -263,7 +263,7 @@ require('tailwindcss-themer')({
```

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand Down Expand Up @@ -332,7 +332,7 @@ require('tailwindcss-themer')({
```

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand All @@ -345,7 +345,7 @@ require('tailwindcss-themer')({
```

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand Down Expand Up @@ -388,10 +388,10 @@ In order to prevent a [flash of unstyled content](https://css-tricks.com/flash-o

### Simultaneous themes

Because this plugin enables themes based on existance of classes, it is possible to have multiple themes enabled at the same time. They can be overlapping or not. Its all up to how you apply the classes!
Because this plugin enables themes in part based on existance of classes, it is possible to have multiple themes enabled at the same time. They can be overlapping or not. Its all up to how you apply the classes!

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand Down Expand Up @@ -656,7 +656,7 @@ As specified above, variants are generated for every named theme you make, even
The theme variant generated for the default theme is `defaultTheme` (e.g. `defaultTheme:rounded-sm`), but this now requires that instead of omitting any theme class to enable the default theme, you explicitly declare you are using the default theme by adding the class of `defaultTheme` to the place you want themed (no other feature is affected by this, using the default theme variant is the only feature that requires you to add the `defaultTheme` class to use). This is because I haven't been able to create a css selector that excludes all parents with any of the other theme classes. If you can make one, feel free to [open up an issue](https://github.com/RyanClementsHax/tailwindcss-themer/issues).

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand All @@ -670,7 +670,7 @@ The theme variant generated for the default theme is `defaultTheme` (e.g. `defau
```

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand All @@ -683,7 +683,7 @@ The theme variant generated for the default theme is `defaultTheme` (e.g. `defau
```

```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
Expand Down
9 changes: 7 additions & 2 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,24 @@ export default defineConfig({

/* Configure projects for major browsers */
projects: [
// Runs before all other projects to initialize all int tests builds without concurrency problems
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
},

{
name: 'firefox',
use: { ...devices['Desktop Firefox'] }
use: { ...devices['Desktop Firefox'] },
// Counts on the first project to initialize all int test builds to reuse for a performance boost
dependencies: ['chromium']
},

{
name: 'webkit',
use: { ...devices['Desktop Safari'] }
use: { ...devices['Desktop Safari'] },
// Counts on the first project to initialize all int test builds to reuse for a performance boost
dependencies: ['chromium']
}

/* Test against mobile viewports. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import serialize from 'serialize-javascript'
import getPort from 'get-port'
import { MultiThemePluginOptions } from '@/utils/optionsUtils'
import { createIsolatedIntTest } from '.'
import { createIsolatedIntTest, parseClasses } from '.'
import { type Config as TailwindConfig } from 'tailwindcss'

export interface OpenOptions {
projectName: string
instanceId: number
titlePath: string[]
}

Expand All @@ -13,36 +14,44 @@ export async function openWithConfig(
options: OpenOptions
): Promise<{ url: string; stop: () => void }> {
const tmpDirName = [
...options.titlePath.map(x => x.replace(/ /g, '-').replace(/\./, '-')),
options.projectName
].join('_')
const test = await createIsolatedIntTest({
...options.titlePath.map(x => x.replace(/ /g, '_')),
options.instanceId
].join('-')

const { test, isAlreadyInitialized } = await createIsolatedIntTest({
RyanClementsHax marked this conversation as resolved.
Show resolved Hide resolved
template: 'create-react-app',
tmpDirName
})

const { filePath: tailwindConfigFilePath } = await test.writeFile(
'tailwind.test.config.js',
`module.exports = {
...${JSON.stringify({
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {}
}
})},
plugins: [require('tailwindcss-themer')(${serialize(config)})]
}`
)

const buildDir = test.getBuildDir()

await test.build({
command: ['npm', ['run', 'build']],
env: {
TAILWIND_CONFIG_PATH: tailwindConfigFilePath,
BUILD_PATH: buildDir
if (!isAlreadyInitialized) {
const classesToPreventPurging = parseClasses(config)

const tailwindConfig: TailwindConfig = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
safelist: classesToPreventPurging,
theme: {
extend: {}
}
}
})

const { filePath: tailwindConfigFilePath } = await test.writeFile(
'tailwind.test.config.js',
`module.exports = {
...${JSON.stringify(tailwindConfig)},
plugins: [require('tailwindcss-themer')(${serialize(config)})]
}`
)

await test.build({
command: ['npm', ['run', 'build']],
env: {
TAILWIND_CONFIG_PATH: tailwindConfigFilePath,
BUILD_PATH: buildDir
}
})
}

const port = await getPort()
const { stop } = await test.startServer({
Expand Down
34 changes: 23 additions & 11 deletions e2e/test_repos/repos/index.ts → e2e/test_repos/drivers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from 'node:path'
import fse from 'fs-extra'
import { spawn } from 'cross-spawn'
import { $, execa } from 'execa'
import { MultiThemePluginOptions } from '@/utils/optionsUtils'

// based off of https://github.com/remix-run/remix/blob/6a9b8d6b836f05a47af9ca6e6f1f3898a2fba8ec/integration/helpers/create-fixture.ts

Expand Down Expand Up @@ -31,29 +32,32 @@ export interface IsolatedIntTestOptions {
template: Template
}

const tmpDirNameRegex = /^[a-zA-Z_\-.]+$/
const tmpDirNameRegex = /^[a-zA-Z0-9_,\-.]+$/

export async function createIsolatedIntTest(
options: IsolatedIntTestOptions
): Promise<IsolatedIntTest> {
): Promise<{ isAlreadyInitialized: boolean; test: IsolatedIntTest }> {
if (!options.tmpDirName.match(tmpDirNameRegex)) {
throw new Error(
`Could not run open test repo with tmp dir name ${options.tmpDirName} because it uses characters not safe for creating a directory name for temporary files. Please use file name safe characters (regex: ${tmpDirNameRegex})`
)
}
const templateDirPath = getTemplateDirPath(options.template)
const testTmpDirPath = getTestTmpDirPath(options.template, options.tmpDirName)
let isAlreadyInitialized = false
if (await fse.exists(testTmpDirPath)) {
throw new Error(
`Was given duplicate tmp dir directory name "${testTmpDirPath}". This would have led to test state bleeding between tests. Please make sure your test title is unique (includes test group names).`
)
isAlreadyInitialized = true
} else {
await fse.ensureDir(testTmpDirPath)
}
return {
isAlreadyInitialized,
test: new IsolatedIntTestImpl({
template: options.template,
testTmpDirPath,
templateDirPath
})
}
await fse.ensureDir(testTmpDirPath)
return new IsolatedIntTestImpl({
template: options.template,
testTmpDirPath,
templateDirPath
})
}

export async function setupTemplates(): Promise<void> {
Expand Down Expand Up @@ -181,3 +185,11 @@ function getTemplateDirPaths(): string[] {
function getTemplateTmpDirPaths(): string[] {
return getTemplates().map(template => getTemplateTmpDirPath(template))
}

export function parseClasses(config: MultiThemePluginOptions): string[] {
const themeNameClasses = config.themes?.map(x => x.name) ?? []
const mediaQueries =
config.themes?.map(x => x.mediaQuery ?? '')?.filter(x => !!x) ?? []
const selectors = config.themes?.flatMap(x => x.selectors ?? []) ?? []
return [...themeNameClasses, ...mediaQueries, ...selectors]
}
2 changes: 1 addition & 1 deletion e2e/test_repos/setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cleanupTmpDirs, setupTemplates } from './repos'
import { cleanupTmpDirs, setupTemplates } from './drivers'

async function setup(): Promise<void> {
await cleanupTmpDirs()
Expand Down
10 changes: 10 additions & 0 deletions e2e/test_repos/templates/create-react-app/dev/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: { extend: {} },
plugins: [
require('tailwindcss-themer')({
defaultTheme: { extend: { colors: { primary: 'blue' } } },
themes: [{ name: 'darkTheme', extend: { colors: { primary: 'red' } } }]
})
]
}
2 changes: 1 addition & 1 deletion e2e/test_repos/templates/create-react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"start": "TAILWIND_CONFIG_PATH=./dev/tailwind.config.js react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
Expand Down
52 changes: 19 additions & 33 deletions e2e/test_repos/templates/create-react-app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,27 @@
import { useMemo, useState } from 'react'

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const preventPurgingOfThemesThatAreSetInTests = [
'darkTheme',
'dark-mode',
'[data-theme="dark"]'
]
import { useState } from 'react'
import { ThemeNode } from './components/ThemeNode'

function App() {
const [attributes, setAttributes] = useState('{}')
const parsedAttributes = useMemo(
() => JSON.parse(attributes) as Record<string, string>,
[attributes]
)
const [numThemeNodes, setNumThemeNodes] = useState(0)
return (
<div {...parsedAttributes}>
<header className="mx-auto flex w-[75ch] flex-col px-5 pt-16">
<h1 className="text-primary text-3xl font-bold">
Mainitainers ❤️ integration tests
<div className="mx-auto flex flex-col px-5 py-16 font-mono text-gray-900">
<header>
RyanClementsHax marked this conversation as resolved.
Show resolved Hide resolved
<h1 className="mb-5 text-center text-4xl font-black">
Mainitainers &lt;3 integration tests
</h1>
<div className="mt-8 flex flex-col gap-2">
<label htmlFor="theme" className="text-sm font-medium text-gray-900">
Attributes
</label>
<input
id="theme"
spellCheck="false"
className="w-32 rounded-md rounded-md border-0 px-3 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"
placeholder="theme"
value={attributes}
onChange={e => setAttributes(e.target.value)}
/>
<pre>
<code>{JSON.stringify(parsedAttributes, null, 2)}</code>
</pre>
</div>
</header>
<main className="mx-auto mt-8 flex w-[75ch] flex-col items-center gap-8">
{Array.from({ length: numThemeNodes }, (_, i) => {
const nodeId = i + 1
return <ThemeNode key={nodeId} nodeId={nodeId} />
})}
<button
className="rounded-md border-0 bg-white px-4 py-3 shadow-md ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 focus-visible:outline-none"
onClick={() => setNumThemeNodes(num => num + 1)}
>
Add theme node
</button>
</main>
</div>
)
}
Expand Down
Loading
Loading