| 1 |
import { DescriptionGathering } from '@auto-launch/components/DescriptionGathering'; |
| 2 |
import { render, screen } from '@testing-library/react'; |
| 3 |
import userEvent from '@testing-library/user-event'; |
| 4 |
|
| 5 |
jest.mock('@auto-launch/hooks/useInstallRequiredPlugins', () => ({ |
| 6 |
useInstallRequiredPlugins: jest.fn(), |
| 7 |
})); |
| 8 |
jest.mock('@auto-launch/functions/helpers', () => ({ |
| 9 |
fetchWithTimeout: jest.fn(), |
| 10 |
})); |
| 11 |
jest.mock('@auto-launch/icons', () => ({ loaderThreeDots: null })); |
| 12 |
const mockLaunchStore = { |
| 13 |
setData: jest.fn(), |
| 14 |
descriptionBackup: '', |
| 15 |
urlParams: {}, |
| 16 |
}; |
| 17 |
jest.mock('@auto-launch/state/launch-data', () => ({ |
| 18 |
useLaunchDataStore: () => mockLaunchStore, |
| 19 |
})); |
| 20 |
jest.mock('@shared/state/ai-consent', () => ({ |
| 21 |
useAIConsentStore: () => ({ consentTerms: '' }), |
| 22 |
})); |
| 23 |
|
| 24 |
const submitButton = () => |
| 25 |
screen.getAllByRole('button').find((b) => b.type === 'submit'); |
| 26 |
|
| 27 |
const descriptionBox = () => |
| 28 |
screen |
| 29 |
.getByPlaceholderText(/A personal photography portfolio/i) |
| 30 |
.closest('.rounded-3xl'); |
| 31 |
|
| 32 |
const titleInput = () => |
| 33 |
screen.queryByPlaceholderText(/Enter your website name/i); |
| 34 |
|
| 35 |
const setAssignments = (assignments) => { |
| 36 |
window.extLaunchData.activeTests = Object.fromEntries( |
| 37 |
Object.entries(assignments).map(([key, variant]) => [key, { variant }]), |
| 38 |
); |
| 39 |
}; |
| 40 |
|
| 41 |
beforeEach(() => { |
| 42 |
window.extLaunchData = { urlParams: {}, activeTests: {} }; |
| 43 |
mockLaunchStore.urlParams = {}; |
| 44 |
}); |
| 45 |
|
| 46 |
afterEach(() => { |
| 47 |
delete window.extLaunchData; |
| 48 |
delete window.extSharedData; |
| 49 |
mockLaunchStore.urlParams = {}; |
| 50 |
}); |
| 51 |
|
| 52 |
describe('DescriptionGathering — AutoLaunch.ShowTitle', () => { |
| 53 |
test('renders no title input for variant A', () => { |
| 54 |
setAssignments({ 'AutoLaunch.ShowTitle': 'A' }); |
| 55 |
render(<DescriptionGathering />); |
| 56 |
expect(titleInput()).not.toBeInTheDocument(); |
| 57 |
}); |
| 58 |
|
| 59 |
test('renders no title input when no assignment is present', () => { |
| 60 |
render(<DescriptionGathering />); |
| 61 |
expect(titleInput()).not.toBeInTheDocument(); |
| 62 |
}); |
| 63 |
|
| 64 |
test('gates submit on the description for variant A', async () => { |
| 65 |
const user = userEvent.setup(); |
| 66 |
render(<DescriptionGathering />); |
| 67 |
expect(submitButton()).toBeDisabled(); |
| 68 |
await user.type(screen.getByRole('textbox'), 'a description'); |
| 69 |
expect(submitButton()).toBeEnabled(); |
| 70 |
}); |
| 71 |
|
| 72 |
test('renders the site-title input and both field labels for variant B', () => { |
| 73 |
setAssignments({ 'AutoLaunch.ShowTitle': 'B' }); |
| 74 |
render(<DescriptionGathering />); |
| 75 |
expect(titleInput()).toBeInTheDocument(); |
| 76 |
expect(screen.getByText('Website title (required)')).toBeInTheDocument(); |
| 77 |
expect(screen.getByText('Describe your website')).toBeInTheDocument(); |
| 78 |
}); |
| 79 |
|
| 80 |
test('gates submit on the title, ignoring an empty description, for variant B', async () => { |
| 81 |
const user = userEvent.setup(); |
| 82 |
setAssignments({ 'AutoLaunch.ShowTitle': 'B' }); |
| 83 |
render(<DescriptionGathering />); |
| 84 |
expect(submitButton()).toBeDisabled(); |
| 85 |
await user.type(titleInput(), 'My Bakery'); |
| 86 |
expect(submitButton()).toBeEnabled(); |
| 87 |
}); |
| 88 |
|
| 89 |
test('pre-fills the title with the decoded blogname', () => { |
| 90 |
setAssignments({ 'AutoLaunch.ShowTitle': 'B' }); |
| 91 |
window.extSharedData = { siteTitle: 'Joe's Diner' }; |
| 92 |
render(<DescriptionGathering />); |
| 93 |
expect(titleInput()).toHaveValue("Joe's Diner"); |
| 94 |
}); |
| 95 |
|
| 96 |
test('leaves the title blank when the blogname is a URL', () => { |
| 97 |
setAssignments({ 'AutoLaunch.ShowTitle': 'B' }); |
| 98 |
window.extSharedData = { siteTitle: 'https://example.com' }; |
| 99 |
render(<DescriptionGathering />); |
| 100 |
expect(titleInput()).toHaveValue(''); |
| 101 |
}); |
| 102 |
|
| 103 |
test('lets a ?title= param override the blogname prefill', () => { |
| 104 |
setAssignments({ 'AutoLaunch.ShowTitle': 'B' }); |
| 105 |
window.extSharedData = { siteTitle: 'Sweet Spot Bakery' }; |
| 106 |
mockLaunchStore.urlParams = { title: 'From URL Param' }; |
| 107 |
render(<DescriptionGathering />); |
| 108 |
expect(titleInput()).toHaveValue('From URL Param'); |
| 109 |
}); |
| 110 |
|
| 111 |
test('autofocuses the description when the title field is hidden (variant A)', () => { |
| 112 |
setAssignments({ 'AutoLaunch.ShowTitle': 'A' }); |
| 113 |
render(<DescriptionGathering />); |
| 114 |
expect( |
| 115 |
screen.getByPlaceholderText(/A personal photography portfolio/i), |
| 116 |
).toHaveFocus(); |
| 117 |
}); |
| 118 |
|
| 119 |
test('autofocuses the title field, not the description, when it is shown (variant B)', () => { |
| 120 |
setAssignments({ 'AutoLaunch.ShowTitle': 'B' }); |
| 121 |
render(<DescriptionGathering />); |
| 122 |
expect(titleInput()).toHaveFocus(); |
| 123 |
expect( |
| 124 |
screen.getByPlaceholderText(/A personal photography portfolio/i), |
| 125 |
).not.toHaveFocus(); |
| 126 |
}); |
| 127 |
}); |
| 128 |
|
| 129 |
describe('DescriptionGathering — AutoLaunch.SubmitOutside', () => { |
| 130 |
test('keeps the submit button inside the input box for variant A', () => { |
| 131 |
render(<DescriptionGathering />); |
| 132 |
expect(descriptionBox()).toContainElement(submitButton()); |
| 133 |
}); |
| 134 |
|
| 135 |
test('renders the submit button below the input box for variant B', () => { |
| 136 |
setAssignments({ 'AutoLaunch.SubmitOutside': 'B' }); |
| 137 |
render(<DescriptionGathering />); |
| 138 |
expect(descriptionBox()).not.toContainElement(submitButton()); |
| 139 |
}); |
| 140 |
}); |
| 141 |
|
| 142 |
describe('DescriptionGathering — AutoLaunch.SubmitCreateWebsite', () => { |
| 143 |
test('labels the submit button "Next" for variant A', () => { |
| 144 |
render(<DescriptionGathering />); |
| 145 |
expect(submitButton()).toHaveTextContent('Next'); |
| 146 |
}); |
| 147 |
|
| 148 |
test('labels the submit button "Create website" for variant B', () => { |
| 149 |
setAssignments({ 'AutoLaunch.SubmitCreateWebsite': 'B' }); |
| 150 |
render(<DescriptionGathering />); |
| 151 |
expect(submitButton()).toHaveTextContent('Create website'); |
| 152 |
}); |
| 153 |
}); |
| 154 |
|
| 155 |
describe('DescriptionGathering — AutoLaunch.HideEnhanceAI', () => { |
| 156 |
test('shows the Enhance with AI button for variant A', () => { |
| 157 |
render(<DescriptionGathering />); |
| 158 |
expect( |
| 159 |
screen.getByRole('button', { name: /Enhance with AI/i }), |
| 160 |
).toBeInTheDocument(); |
| 161 |
}); |
| 162 |
|
| 163 |
test('hides the Enhance with AI button for variant B', () => { |
| 164 |
setAssignments({ 'AutoLaunch.HideEnhanceAI': 'B' }); |
| 165 |
render(<DescriptionGathering />); |
| 166 |
expect( |
| 167 |
screen.queryByRole('button', { name: /Enhance with AI/i }), |
| 168 |
).not.toBeInTheDocument(); |
| 169 |
}); |
| 170 |
}); |
| 171 |
|
| 172 |
describe('DescriptionGathering — AutoLaunch.DescriptionPlaceholderLaw', () => { |
| 173 |
test('uses the photography placeholder for variant A', () => { |
| 174 |
render(<DescriptionGathering />); |
| 175 |
expect( |
| 176 |
screen.getByPlaceholderText(/A personal photography portfolio/i), |
| 177 |
).toBeInTheDocument(); |
| 178 |
}); |
| 179 |
|
| 180 |
test('uses the law-firm placeholder for variant B', () => { |
| 181 |
setAssignments({ 'AutoLaunch.DescriptionPlaceholderLaw': 'B' }); |
| 182 |
render(<DescriptionGathering />); |
| 183 |
expect( |
| 184 |
screen.getByPlaceholderText(/A boutique law firm/i), |
| 185 |
).toBeInTheDocument(); |
| 186 |
}); |
| 187 |
}); |
| 188 |
|