feat: expand outreach targeting and messaging
This commit is contained in:
1 parent
7defff422d
commit
d0a8bd0e2f
40 files changed
+890
-82
No files matched your search
@@ -113,6 +113,7 @@ describe('dashboard foundation', () => {
|
||||
|
||||
it('shows the live outreach stages and the potential posts being burned down', async () => {
|
||||
installSuccessfulApi();
|
||||
const user = userEvent.setup();
|
||||
server.use(
|
||||
http.get('/api/login/status', () => HttpResponse.json({ ready: true, loggedIn: true, status: 'authenticated' })),
|
||||
http.get('/api/chats/sync', () => HttpResponse.json({
|
||||
@@ -131,6 +132,17 @@ describe('dashboard foundation', () => {
|
||||
status: 'ready', priority: 2, postCreatedAt: Date.parse('2026-08-09T06:50:00Z'),
|
||||
discoveredAt: Date.parse('2026-08-09T07:00:00Z'), updatedAt: Date.parse('2026-08-09T07:30:00Z'),
|
||||
availableAt: Date.parse('2026-08-09T07:30:00Z'), attemptCount: 1, hasPreparedMessage: true,
|
||||
physiqueScore: 6,
|
||||
preparation: {
|
||||
message: 'You look incredible in that pose — what inspired this post?',
|
||||
imageAnalysis: 'VISIBLE_FACTS:\nBODY: VISIBLE - full-body pose',
|
||||
detectedGender: 'female', attractivenessScore: 68, physiqueScore: 6,
|
||||
physiqueTargeting: { enabled: true, minScore: 4, maxScore: 7 },
|
||||
sourceImageUrl: 'https://i.redd.it/candidate-1.jpg',
|
||||
strategyId: 'direct-observation', experimentId: 'strategies:direct-observation:abc123',
|
||||
promptVersion: 'abc123', selectionReason: 'exploit', modelId: 'example/image-model',
|
||||
toneId: 'direct', scripted: false,
|
||||
},
|
||||
note: null, owner: null,
|
||||
}],
|
||||
})),
|
||||
@@ -141,11 +153,21 @@ describe('dashboard foundation', () => {
|
||||
expect(await screen.findByRole('heading', { name: 'Outreach funnel' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { name: '3 active posts' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('list', { name: 'Active outreach candidates' })).toHaveTextContent('A potential post');
|
||||
expect(screen.getByRole('link', { name: '[25F] A potential post' })).toHaveAttribute(
|
||||
'href',
|
||||
'https://www.reddit.com/r/example/comments/candidate-1/',
|
||||
);
|
||||
expect(screen.getAllByRole('link', { name: '[25F] A potential post' }))
|
||||
.toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ href: 'https://www.reddit.com/r/example/comments/candidate-1/' }),
|
||||
]));
|
||||
expect(within(screen.getByRole('list', { name: 'Active outreach candidates' })).getByText('Message prepared')).toBeInTheDocument();
|
||||
const preparation = screen.getByRole('region', { name: 'Prepared outreach for u/alice' });
|
||||
expect(preparation).toHaveTextContent('You look incredible in that pose');
|
||||
expect(preparation).toHaveTextContent('68/100');
|
||||
expect(preparation).toHaveTextContent('6/10');
|
||||
expect(preparation).toHaveTextContent('Target 4–7');
|
||||
expect(preparation).toHaveTextContent('example/image-model');
|
||||
const sourceImage = within(preparation).getByRole('img', { name: 'Source post: [25F] A potential post' });
|
||||
expect(sourceImage).toHaveClass('is-blurred');
|
||||
await user.click(within(preparation).getByRole('button', { name: 'Reveal source image: [25F] A potential post' }));
|
||||
expect(sourceImage).not.toHaveClass('is-blurred');
|
||||
});
|
||||
|
||||
it('hydrates media filters from the routed URL', async () => {
|
||||
|
||||
@@ -13,7 +13,12 @@ import { SettingsRoute } from '../../apps/dashboard/src/features/settings/settin
|
||||
const config = {
|
||||
rateLimit: { minDelayBetweenMessagesMs: 300_000, maxMessagesPerHour: 8, jitterMs: 60_000 },
|
||||
browser: { dryRun: true },
|
||||
reddit: { prioritizeRecentPosts: false, recentWindowMinutes: 60, maxReadyCandidates: 25 },
|
||||
reddit: {
|
||||
prioritizeRecentPosts: false,
|
||||
recentWindowMinutes: 60,
|
||||
maxReadyCandidates: 25,
|
||||
physiqueTargeting: { enabled: false, minScore: 1, maxScore: 10 },
|
||||
},
|
||||
subreddits: [{ name: 'casualconversation', type: 'chat', weight: 2, tone: 'default' }],
|
||||
tones: ['default', 'dominant', 'submissive', 'playful', 'direct', 'gentle', 'kink-friendly', 'teasing'],
|
||||
advanced: { openrouter: { chatModel: 'example/chat-model' } },
|
||||
@@ -204,6 +209,46 @@ describe('settings and access', () => {
|
||||
expect(screen.queryByText('Settings saved.')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('validates and saves the physique targeting range', async () => {
|
||||
installConfigApi();
|
||||
let requestBody: { reddit?: typeof config.reddit } | undefined;
|
||||
server.use(http.put('/api/config', async ({ request }) => {
|
||||
requestBody = await request.json() as typeof requestBody;
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
config: {
|
||||
rateLimit: config.rateLimit,
|
||||
browser: config.browser,
|
||||
reddit: requestBody?.reddit ?? config.reddit,
|
||||
},
|
||||
subreddits: config.subreddits,
|
||||
});
|
||||
}));
|
||||
const user = userEvent.setup();
|
||||
renderSettings('/settings?section=monitoring');
|
||||
|
||||
await user.click(await screen.findByRole('checkbox', { name: 'Enable physique targeting' }));
|
||||
const minimum = screen.getByRole('spinbutton', { name: 'Minimum physique score' });
|
||||
const maximum = screen.getByRole('spinbutton', { name: 'Maximum physique score' });
|
||||
await user.clear(minimum);
|
||||
await user.type(minimum, '7');
|
||||
await user.clear(maximum);
|
||||
await user.type(maximum, '4');
|
||||
await user.click(screen.getByRole('button', { name: 'Save settings' }));
|
||||
expect(screen.getByText('Minimum physique score cannot exceed maximum physique score.')).toBeInTheDocument();
|
||||
expect(requestBody).toBeUndefined();
|
||||
|
||||
await user.clear(maximum);
|
||||
await user.type(maximum, '9');
|
||||
await user.click(screen.getByRole('button', { name: 'Save settings' }));
|
||||
|
||||
await waitFor(() => expect(requestBody?.reddit?.physiqueTargeting).toEqual({
|
||||
enabled: true,
|
||||
minScore: 7,
|
||||
maxScore: 9,
|
||||
}));
|
||||
});
|
||||
|
||||
it('validates subreddit rows and confirms disabling dry run before saving behaviour', async () => {
|
||||
installConfigApi();
|
||||
let updateRequests = 0;
|
||||
|
||||
Reference in new issue
Block a user