// Mock localStorage global.localStorage = { getItem: jest.fn(() => "mock-token"), setItem: jest.fn(), removeItem: jest.fn(), clear: jest.fn(), length: 0, key: jest.fn(), }; // Mock BASE_URL jest.mock("../../../api/constants", () => ({ BASE_URL: "https://77.smartvideo.py.qikongjian.com", })); import { getProjectScript, abortVideoTask, pausePlanFlow, resumePlanFlow, } from "../../../api/video_flow"; import {StoryDetails } from "../domain/valueObject"; import { ScriptEditUseCase } from "../usecase/ScriptEditUseCase"; describe("ScriptService 业务逻辑测试", () => { // 创建新的剧本编辑用例 const newScriptEditUseCase = new ScriptEditUseCase(""); const script = "在阳光明媚的码头上,两只柴犬展开了一场薯条吃比赛。一只优雅的母猫担任裁判,端坐高处,威严地监督比赛。两只鸽子站在一旁,歪着头有趣地看着,偶尔咕咕低鸣。柴犬们瞪大眼睛,尾巴摇得飞快,争抢盘子里的金黄薯条。从日出到天黑,它们吃个不停,薯条堆成了小山,母猫无奈摇头,鸽子仍兴致勃勃,场面热闹非凡。"; let projectId: string; // let name: string; it("想法生成剧本", async () => { const res = await newScriptEditUseCase.generateScript(script, (content) => { // console.log(content); console.log(newScriptEditUseCase.getStoryDetails()); }); expect(newScriptEditUseCase.toString()).toBeDefined(); }, 300000); it("创建项目", async () => { const createRes = await newScriptEditUseCase.createProject( script, "user123", "automatic", "720p", "en" ); expect(createRes.project_id).toBeDefined(); projectId = createRes.project_id; }); it("保存剧本", async () => { const res = await newScriptEditUseCase.saveScript(projectId); console.log(res); }); it("应用剧本", async () => { await newScriptEditUseCase.applyScript(projectId); console.log(projectId); }); }); describe("解析测试", () => { const scriptText = `"**Logline**\nAn anxious young man, facing what he believes is a crushing public rejection on a first date, must overcome his deepest fear of inadequacy to take a chance on a real connection.\n\n**Core Elements**\n\n1. **Protagonist:**\n * **Core Identity:** LI WEI (李伟), 25, male, an introverted and socially anxious programmer in a modern metropolis. He is neat but unremarkable, hiding his insecurity behind a pair of glasses and a quiet demeanor.\n * **Initial State & Flaw:** He is at a coffee shop for a first date, fraught with nervous anticipation. His core flaw is a profound **fear of inadequacy and rejection**, a deep-seated belief that he is fundamentally uninteresting and will inevitably disappoint others.\n\n2. **The Inciting Incident:**\n * (State Clearly) A beautiful, confident woman, XIAOQIAN (小倩), whom he believes is his date, enters the coffee shop. However, she sits at a different table and is soon joined by a handsome, charismatic man. Their immediate, easy chemistry confirms Li Wei's worst fears. He has been stood up for someone better, or so he believes. This event is a passive, public humiliation that directly preys on his core flaw.\n\n3. **The Problem & New Goal:**\n * (State Clearly) The problem is no longer about impressing a date. It is a crushing psychological crisis: \"How do I escape this situation without confirming my own deeply-held belief that I am a failure?\" His new, urgent goal is to simply survive the moment and leave with his self-worth intact.\n\n4. **Conflict & Obstacles:**\n * **Primary Conflict:** An intense internal battle between Li Wei's crippling self-doubt and the flicker of a desire to not be defined by it.\n * **Key Obstacles:** 1) The happy couple's laughter, which serves as an auditory torment. 2) The well-meaning WAITRESS, whose attention amplifies his isolation.\n\n5. **The Stakes:**\n * **What is at stake?:** If he succeeds in leaving with composure, he salvages a fragile piece of his dignity. If he fails—fleeing awkwardly or breaking down—he will permanently cement his narrative of failure, making it exponentially harder to ever attempt a social connection again. He will lose the courage to try.\n\n6. **Character Arc Accomplished:**\n * (State the Result Clearly) He confronts his fear of inadequacy head-on. After receiving a text from his *actual* date, who is at the wrong location, he makes a conscious choice. Instead of succumbing to his anxiety and cancelling, he takes a small but monumental step. He deletes a self-deprecating draft, sends a simple, confident reply, and walks out, ready to face the unknown. He has transformed from someone paralyzed by fear into someone willing to act despite it.\n\n**GENRE:** Drama\n\n---\n\n**SCENE 1**\n\nINT. \"CORNER PERCH\" COFFEE SHOP - DAY\n\n**Scene Transition:** An establishing shot of a sun-drenched, stylishly minimalist coffee shop. The camera finds our protagonist, LI WEI, already seated, having arrived early to stake out a \"safe\" spot. The scene is set to capture the quiet, hopeful tension before an anticipated event.\n\nSOUND of distant traffic, the gentle hiss of an espresso machine, soft indie music\n\nThe afternoon sun streams through a large window, illuminating dust motes dancing in the air. The coffee shop is a haven of warm wood and the rich aroma of roasted beans.\n\nLI WEI (25), neat in a plain grey t-shirt, sits alone at a small two-person table. He polishes his glasses with a napkin, an unnecessary, repetitive motion.\n\nAn untouched glass of water sweats onto the table.\n\nCLOSE-UP - LI WEI'S PHONE\nHis thumb hovers over a messaging app. The profile picture of his date, \"XIAOQIAN,\" is a bright, smiling face. He types a message: \"I'm here. At the table by the window.\" He hesitates. Deletes it.\n\nHe forces a casual posture, trying to look absorbed in his phone, but his eyes dart towards the door every time it chimes.\n\n**LI WEI (V.O.)**\nJust be normal. Don't look desperate.\nJust... a guy enjoying coffee. Alone.\n\nHe picks up his water glass, but his hand trembles slightly, rattling the ice. He puts it down too quickly. A little water sloshes over the rim. He dabs at it furiously with a napkin.\n\n---\n\n**SCENE 2**\n\nINT. \"CORNER PERCH\" COFFEE SHOP - DAY\n\n**Scene Transition:** The chiming of the door from the previous scene directly leads to the entrance of a new character. Li Wei's focus, and therefore the camera's, immediately shifts from his internal world to this external arrival, escalating the scene's tension.\n\nThe bell on the door CHIMES.\n\nLi Wei's head snaps up.\n\nXIAOQIAN (24) enters. She is exactly like her profile picture—radiant, dressed in a stylish floral dress. She scans the room.\n\nLi Wei's heart hammers. He makes to raise a hand, a small, aborted wave.\n\nBut her eyes glide right past him. She smiles, a flash of genuine warmth, at a table across the room. She walks over to an empty table for two, not his.\n\nLi Wei's half-raised hand drops to the table with a soft THUD. His face freezes.\n\nHe watches, paralyzed, as she sits down, places her bag on the empty chair, and pulls out her phone. She is waiting.\n\nThe WAITRESS (40s, kindly) approaches Li Wei's table.\n\n**WAITRESS**\nReady to order something else, dear?\nOr still waiting for your friend?\n\n**LI WEI**\n(A whisper)\nStill waiting. Thank you.\n\nThe waitress nods sympathetically and moves away. The words hang in the air, a public declaration of his solitude.\n\nA moment later, the door CHIMES again. A HANDSOME MAN (26), confident and laughing, enters and walks directly to Xiaoqian's table.\n\n**HANDSOME MAN**\nSorry! The traffic was a nightmare.\nYou look amazing.\n\n**XIAOQIAN**\n(Laughing)\nYou're lucky you're so handsome.\nI almost left.\n\nThey lean in, their conversation a low, happy murmur. Their chemistry is instant, effortless.\n\nEXTREME CLOSE-UP - LI WEI'S FACE\nA universe of humiliation plays out in his eyes. He stares down at his phone, the screen now a black mirror reflecting his own strained face.\n\n---\n\n**SCENE 3**\n\nINT. \"CORNER PERCH\" COFFEE SHOP - DAY\n\n**Scene Transition:** Li Wei remains frozen, trapped by the previous scene's devastating turn. The camera stays tight on him, emphasizing his psychological confinement. The passage of time is marked only by the shifting light and the sounds from the other table, creating a slow, agonizing development of his internal state.\n\nSOUND of the couple's intermittent laughter, a sharp, painful punctuation mark.\n\nMinutes crawl by. Li Wei doesn't move. He scrolls aimlessly through his phone, not seeing anything. The screen's glow is the only light on his face, which has gone pale.\n\nHe risks a glance. The couple is now sharing a piece of cake, laughing as the man wipes a smudge of cream from her nose. It is a moment of perfect, casual intimacy.\n\nLi Wei flinches as if struck. He needs to escape.\n\nHe starts drafting a text to a friend.\n\"Worst day ever. You won't believe...\" He stops. Deletes it. The shame is too much to even articulate.\n\n**LI WEI (V.O.)**\nJust go. Get up and walk out.\nWhy can't you just go?\n\nHis body won't obey. He is pinned to his chair by the weight of his own failure. He imagines walking past them, feeling their eyes on his back. The thought is unbearable.\n\nSuddenly, his phone VIBRATES violently on the table. A sharp, loud BUZZ that cuts through the cafe's murmur.\n\nHe startles. The couple looks over for a second. Li Wei's face burns with shame.\n\nHe looks at the screen. A new message. From a number he doesn't recognize, but the profile picture is different. More candid, less polished, but still her. The name reads: \"Qianqian.\"\n\nTHE MESSAGE: \"Hey! So sorry, I'm an idiot! I'm at the 'Corner Cup' not the 'Corner Perch'! They're across the street from each other! Running over now, so so sorry! :( \"\n\nLi Wei stares at the text. His brain reboots.\nThe 'Corner Cup'. Not the 'Corner Perch'.\nThis wasn't rejection. It was... logistics.\n\nA wave of dizzying relief washes over him, followed immediately by a new surge of anxiety. He has to meet her now. After all this.\n\nHe opens a reply, his thumbs shaking.\n\"It's okay but honestly I was about to leave, this has been a really awkward...\"\n\nHe stops. He looks at the words on his screen. A story of defeat.\nHe looks up, past his phone, at the couple across the room. They are just two people. Happy, yes. But just people.\nHe looks at the new message from \"Qianqian.\" An apology. An emoji. A human mistake.\n\nHe slowly, deliberately, deletes his entire draft.\n\n---\n\n**SCENE 4**\n\nINT. \"CORNER PERCH\" COFFEE SHOP - DAY\n\n**Scene Transition:** The decision made at the end of Scene 3 provides the direct motivation for action. The camera pulls back, giving Li Wei space for the first time. His movement from the chair to the door is the scene's entire narrative arc—a physical journey mirroring his internal one.\n\nLi Wei takes a deep breath. It's the first real breath he's taken all day.\n\nCLOSE-UP - HIS THUMBS\nHe types a new message. Short. Simple.\n\nREPLY: \"No worries. I'm at the window. See you soon.\"\n\nHe hits send without hesitating.\n\nThen, he does the impossible. He stands up. He pushes his chair in neatly. He places a few bills on the table for the water.\n\nHe walks towards the door.\nHis path takes him directly past the other couple's table.\nFor a fleeting second, he almost looks at them. But he doesn't. He looks straight ahead, at the door, at the world outside. They are no longer the center of his universe. They are just background noise.\n\nHe pushes the door open. The bell CHIMES, this time a sound of release.\n\nEXT. STREET - DAY\n\n**Scene Transition:** An \"empty shot\" transition. The camera holds on the 'Corner Perch' door as it closes, then PANS across the busy street to the bright, welcoming sign of the 'Corner Cup' cafe on the other side. This visually connects the two locations and bridges the emotional journey.\n\nLi Wei steps out into the bright, noisy street. The sun hits his face. He blinks, adjusting to the light. He looks across the street at the other cafe.\n\nHe takes a step off the curb.\n\n**LI WEI (V.O.)**\nJust a guy.\nGoing to get a coffee.\n\nHe walks across the street, his stride not confident, not perfect, but steady. He is moving forward.\n\nFADE TO BLACK.\n\n---\n\n### Part 2: Addendum for Director & Performance\n\n**【Decoding the Directing Style】**\n\n* **Core Visual Tone:** Employ a style of urban realism with high subjectivity. The color palette inside the 'Perch' should be warm, almost oppressively so, to contrast with Li Wei's cold anxiety. Use a shallow depth of field to isolate Li Wei, blurring the rest of the cafe into an indistinct background until the other couple becomes his sharp, painful focus.\n* **Key Scene Treatment Suggestion:** In Scene 3, during Li Wei's paralysis, use a long, static take. The only movement should be his thumbs on the phone and his darting eyes. The sound design is crucial here: amplify the couple's laughter and the clinking of their silverware until it feels like a psychological attack. The sudden, loud vibration of his phone should be a jump-scare, shattering the agonizing stillness and jolting both him and the audience.\n\n**【The Core Performance Key】**\n\n* **The Character's Physicality:** Li Wei's body language should be a portrait of containment. His shoulders are slightly hunched, his movements are small and economical as if trying to take up less space. In Scene 1, his fidgeting with the napkin and glass isn't just nervousness; it's a desperate attempt to control an environment he feels has no place for him. His transformation in Scene 4 is marked by the simple act of standing up straight and walking with a directed purpose, not looking down.\n* **Subtextual Drive:** The subtext of Li Wei's internal monologue and actions until the final text is: \"Confirm my inadequacy so I can go home.\" He is subconsciously looking for proof that he should give up. The performance must show this self-sabotage. The final text message, \"See you soon,\" has the subtext: \"I am willing to be proven wrong about myself.\"\n\n**【Connection to the Zeitgeist】**\n\n* This story of perceived public failure and digital miscommunication resonates deeply in an age of curated online identities, where the gap between our idealized digital selves and our anxious, real-world selves creates constant, quiet friction."`; let protagonistText = "", incitingIncidentText = "", categories: string[] = [], protagonist = "", problem = "", conflict = "", stakes = ""; const s = new StoryDetails(""); it("剧本解析", async () => { protagonistText = s.extractContentByHeader(scriptText, "Logline"); console.log("protagonistText", protagonistText); incitingIncidentText = s.extractContentByHeader( scriptText, "The Inciting Incident" ); console.log("incitingIncidentText", incitingIncidentText); // --- 其他字段的提取保持不变,使用标题进行查找 --- categories = s .extractContentByHeader(scriptText, "GENRE") .split(",") // .map((s) => s.trim()) // .filter((s) => s.length > 0); console.log("categories", categories); protagonist = s.extractContentByHeader(scriptText, "Core Identity:"); console.log("protagonist", protagonist); problem = s.extractContentByHeader(scriptText, "The Problem & New Goal"); console.log("problem", problem); conflict = s.extractContentByHeader(scriptText, "Conflict & Obstacles"); console.log("conflict", conflict); stakes = s.extractContentByHeader(scriptText, "The Stakes"); console.log("stakes", stakes); }); }); describe("剧本功能对接测试",()=>{ it("初始化 解析剧本" , async()=>{ const response = await getProjectScript({ project_id: "21f194df-cb4b-4e3a-8d44-ca14f23fd1c2" }); const newScriptEditUseCase = new ScriptEditUseCase(response.data.generated_script); }) it("分类解析",()=>{ // 测试代码 const testText = `**GENRE:** Drama\n\n---\n\n**SCENE 1**`; const s = new StoryDetails(""); const result = s.extractContentByHeader(testText, "GENRE", true); console.log("测试结果:", result); // 应该输出 "Drama" }) } )