#![allow(unused_must_use)]

use super::*;
use serde_json::to_string_pretty;
use swc_atoms::Atom;

macro_rules! snapshot_res {
	($res: expr, $prefix: expr) => {
		match $res {
			Ok(v) => {
				let mut output: String = $prefix;

				for module in &v.modules {
					let is_entry = if module.is_entry { "(ENTRY POINT)" } else { "" };
					output += format!(
						"\n============================= {} {}==\n\n{}\n\n{:?}",
						module.path, is_entry, module.code, module.map
					)
					.as_str();
					if let Some(segment) = &module.segment {
						let segment = to_string_pretty(&segment).unwrap();
						output += &format!("\n/*\n{}\n*/", segment);
					}
				}
				output += format!(
					"\n== DIAGNOSTICS ==\n\n{}",
					to_string_pretty(&v.diagnostics).unwrap()
				)
				.as_str();
				insta::assert_snapshot!(output);
			}
			Err(err) => {
				insta::assert_snapshot!(err);
			}
		}
	};
}

macro_rules! test_input {
	($input: expr) => {{
		let input = $input;
		let code = input.code.to_string();
		let snapshot = input.snapshot;
		let res = test_input_fn(input);
		if snapshot {
			snapshot_res!(&res, format!("==INPUT==\n\n{}", code.to_string()));
		}
		res
	}};
}

fn test_input_fn(input: TestInput) -> Result<TransformOutput, anyhow::Error> {
	let strip_exports: Option<Vec<Atom>> = input
		.strip_exports
		.map(|v| v.into_iter().map(|s| Atom::from(s)).collect());
	let reg_ctx_name: Option<Vec<Atom>> = input
		.reg_ctx_name
		.map(|v| v.into_iter().map(|s| Atom::from(s)).collect());
	let strip_ctx_name: Option<Vec<Atom>> = input
		.strip_ctx_name
		.map(|v| v.into_iter().map(|s| Atom::from(s)).collect());

	transform_modules(TransformModulesOptions {
		src_dir: input.src_dir,
		root_dir: input.root_dir,
		input: vec![TransformModuleInput {
			code: input.code.clone(),
			path: input.filename,
			dev_path: input.dev_path,
		}],
		source_maps: true,
		minify: input.minify,
		transpile_ts: input.transpile_ts,
		transpile_jsx: input.transpile_jsx,
		preserve_filenames: input.preserve_filenames,
		explicit_extensions: input.explicit_extensions,
		entry_strategy: input.entry_strategy,
		mode: input.mode,
		scope: input.scope,
		core_module: input.core_module,
		strip_exports,
		strip_ctx_name,
		reg_ctx_name,
		strip_event_handlers: input.strip_event_handlers,
		is_server: input.is_server,
		// filler to maintain line offsets
	})
}

#[test]
fn example_1() {
	test_input!(TestInput {
		code: r#"
import { $, component, onRender } from '@qwik.dev/core';

export const renderHeader1 = $(() => {
	return (
		<div onClick={$((ctx) => console.log(ctx))}/>
	);
});
const renderHeader2 = component($(() => {
	console.log("mount");
	return render;
}));
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_2() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';
export const Header = component$(() => {
	console.log("mount");
	return (
		<div onClick={$((ctx) => console.log(ctx))}/>
	);
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_3() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';
export const App = () => {
	const Header = component$(() => {
		console.log("mount");
		return (
			<div onClick={$((ctx) => console.log(ctx))}/>
		);
	});
	return Header;
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_4() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';
export function App() {
	const Header = component$(() => {
		console.log("mount");
		return (
			<div onClick={$((ctx) => console.log(ctx))}/>
		);
	});
	return Header;
}
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_5() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';
export const Header = component$(() => {
	return (
		<>
			<div onClick={(ctx) => console.log("1")}/>
			<div onClick={$((ctx) => console.log("2"))}/>
		</>
	);
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_6() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';
export const sym1 = $((ctx) => console.log("1"));
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_7() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';

export const Header = component$(() => {
	console.log("mount");
	return (
		<div onClick={$((ctx) => console.log(ctx))}/>
	);
	});

const App = component$(() => {
	return (
		<Header/>
	);
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_8() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';

export const Header = component$(() => {
	return $((hola) => {
		const hola = this;
		const {something, styff} = hola;
		const hello = hola.nothere.stuff[global];
		return (
			<Header/>
		);
	});
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_9() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';
const Header = $((decl1, {decl2}, [decl3]) => {
	const {decl4, key: decl5} = this;
	let [decl6, ...decl7] = stuff;
	const decl8 = 1, decl9;
	function decl10(decl11, {decl12}, [decl13]) {}
	class decl14 {
		method(decl15, {decl16}, [decl17]) {}
	}
	try{}catch(decl18){}
	try{}catch({decl19}){}
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_10() {
	test_input!(TestInput {
		filename: "project/test.tsx".to_string(),
		code: r#"
import { $, component$ } from '@qwik.dev/core';
const Header = $((decl1, {decl2}, [decl3]) => {

	const hola = ident1.no;
	ident2;
	const a = ident1 + ident3;
	const b = ident1 + ident3;
	ident4(ident5, [ident6], {ident7}, {key: ident8});
	class Some {
		prop = ident9;
		method() {
			return ident10;
		}
	}

	return (
		<div onClick={(ident11) => ident11 + ident12} required={false}/>
	)
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_11() {
	test_input!(TestInput {
		filename: "project/test.tsx".to_string(),
		code: r#"
import { $, component$ } from '@qwik.dev/core';
import {foo, bar as bbar} from "../state";
import * as dep2 from "dep2";
import dep3 from "dep3/something";

export const Header = component$(() => {
	return (
		<Header onClick={$((ev) => dep3(ev))}>
			{dep2.stuff()}{bbar()}
		</Header>
	);
});

export const App = component$(() => {
	return (
		<Header>{foo()}</Header>
	);
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Single,
		..TestInput::default()
	});
}

#[test]
fn example_functional_component() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useStore } from '@qwik.dev/core';
const Header = component$(() => {
	const thing = useStore();
	const {foo, bar} = foo();

	return (
		<div>{thing}</div>
	);
});
"#
		.to_string(),
		minify: MinifyMode::None,
		..TestInput::default()
	});
}

#[test]
fn example_functional_component_2() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useStore } from '@qwik.dev/core';
export const useCounter = () => {
	return useStore({count: 0});
}

export const STEP = 1;

export const App = component$((props) => {
	const state = useCounter();
	const thing = useStore({thing: 0});
	const STEP_2 = 2;

	const count2 = state.count * 2;
	return (
		<div onClick$={() => state.count+=count2 }>
			<span>{state.count}</span>
			{buttons.map(btn => (
				<button
					onClick$={() => state.count += btn.offset + thing + STEP + STEP_2 + props.step}
				>
					{btn.name}
				</button>
			))}

		</div>

	);
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_functional_component_capture_props() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useStore } from '@qwik.dev/core';

export const App = component$(({count, rest: [I2, {I3, v1: [I4], I5=v2, ...I6}, I7=v3, ...I8]}) => {
	const state = useStore({count: 0});
	const {rest: [C2, {C3, v1: [C4], C5=v2, ...C6}, C7=v3, ...C8]} = foo();
	return $(() => {
		return (
			<div onClick$={() => state.count += count + total }>
				{I2}{I3}{I4}{I5}{I6}{I7}{I8}
				{C2}{C3}{C4}{C5}{C6}{C7}{C8}
				{v1}{v2}{v3}
			</div>
		)
	});
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_multi_capture() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';

export const Foo = component$(({foo}) => {
	const arg0 = 20;
	return $(() => {
		const fn = ({aaa}) => aaa;
		return (
			<div>
				{foo}{fn()}{arg0}
			</div>
		)
	});
})

export const Bar = component$(({bar}) => {
	return $(() => {
		return (
			<div>
				{bar}
			</div>
		)
	});
})
"#
		.to_string(),
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn example_dead_code() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';
import { deps } from 'deps';

export const Foo = component$(({foo}) => {
	useMount$(() => {
		if (false) {
			deps();
		}
	});
	return (
		<div />
	);
})
"#
		.to_string(),
		minify: MinifyMode::Simplify,
		..TestInput::default()
	});
}

#[test]
fn example_with_tagname() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';

export const Foo = component$(() => {
	return $(() => {
		return (
			<div>
			</div>
		)
	});
}, {
	tagName: "my-foo",
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_with_style() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useStyles$ } from '@qwik.dev/core';

export const Foo = component$(() => {
	useStyles$('.class {}');
	return (
		<div class="class"/>
	);
}, {
	tagName: "my-foo",
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_props_optimization() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useTask$ } from '@qwik.dev/core';
import { CONST } from 'const';
export const Works = component$(({
	count,
	some = 1+2,
	hello = CONST,
	stuff: hey,
	stuffDefault: hey2 = 123,
	...rest}) => {
	console.log(hey, some);
	useTask$(({track}) => {
		track(() => count);
		console.log(count, rest, hey, some, hey2);
	});
	return (
		<div some={some} params={{ some }} class={count} {...rest} override>{count}</div>
	);
});

export const NoWorks2 = component$(({count, stuff: {hey}}) => {
	console.log(hey);
	useTask$(({track}) => {
		track(() => count);
		console.log(count);
	});
	return (
		<div class={count}>{count}</div>
	);
});

export const NoWorks3 = component$(({count, stuff = hola()}) => {
	console.log(stuff);
	useTask$(({track}) => {
		track(() => count);
		console.log(count);
	});
	return (
		<div class={count}>{count}</div>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn example_props_wrapping() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal } from '@qwik.dev/core';
export const Works = component$(({fromProps}) => {
	let fromLocal = useSignal(0);
	return (
		<div
			computed={fromLocal + fromProps}
			local={fromLocal}
			props-wrap={fromProps}
			props-only={{props: fromProps}}
			props={{props: fromProps, local: fromLocal}}
				>
		</div>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn example_props_wrapping2() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal } from '@qwik.dev/core';
export const Works = component$((props: { fromProps: number }) => {
	let fromLocal = useSignal(0);
	return (
		<div
			computed={fromLocal + props.fromProps}
			local={fromLocal}
			props-wrap={props.fromProps}
			props-only={{props: props.fromProps}}
			props={{props: props.fromProps, local: fromLocal}}
				>
		</div>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn example_props_wrapping_children() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal } from '@qwik.dev/core';
export const Works = component$(({fromProps}) => {
	let fromLocal = useSignal(0);
	return (
		<div>
			{fromLocal}
			{fromProps}
			{fromLocal + fromProps}
			{{props: fromProps}}
			{{local: fromLocal}}
			{{props: fromProps, local: fromLocal}}
		</div>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn example_props_wrapping_children2() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal } from '@qwik.dev/core';
export const Works = component$((props) => {
	let fromLocal = useSignal(0);
	return (
		<div>
		  before-
			{fromLocal}
			{props.fromProps}
			{fromLocal + props.fromProps}
			{{props: props.fromProps}}
			{{local: fromLocal}}
			{{props: props.fromProps, local: fromLocal}}
			-after
		</div>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn example_use_optimization() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useTask$ } from '@qwik.dev/core';
import { CONST } from 'const';
export const Works = component$((props) => {
	const {countNested} = useStore({value:{count:0}}).value;
	const countNested2 = countNested;
	const {hello} = countNested2;
	const bye = hello.bye;
	const {ciao} = bye.italian;


	return (
		<div ciao={ciao} >{foo}</div>
	);
});
"#
		.to_string(),
		transpile_jsx: false,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		is_server: Some(false),
		..TestInput::default()
	});
}

#[test]
fn example_optimization_issue_3561() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const Issue3561 = component$(() => {
	const props = useStore({
		product: {
		currentVariant: {
			variantImage: 'image',
			variantNumber: 'number',
			setContents: 'contents',
		},
		},
	});
	const {
		currentVariant: { variantImage, variantNumber, setContents } = {},
	} = props.product;

	console.log(variantImage, variantNumber, setContents)

	return <p></p>;
	});
"#
		.to_string(),
		transpile_jsx: false,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		is_server: Some(false),
		..TestInput::default()
	});
}

#[test]
fn example_optimization_issue_4386() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const FOO_MAPPING = {
	A: 1,
	B: 2,
	C: 3,
	};

	export default component$(() => {
	const key = 'A';
	const value = FOO_MAPPING[key];

	return <>{value}</>;
	});
"#
		.to_string(),
		transpile_jsx: false,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		is_server: Some(false),
		..TestInput::default()
	});
}

#[test]
fn example_optimization_issue_3542() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const AtomStatus = component$(({ctx,atom})=>{
	let status = atom.status;
	if(!atom.real) {
		status="WILL-VANISH"
	} else if (JSON.stringify(atom.atom)==JSON.stringify(atom.real)) {
		status="WTFED"
	}
	return (
		<span title={atom.ID} onClick$={(ev)=>atomStatusClick(ctx,ev,[atom])} class={["atom",status,ctx.store[atom.ID]?"selected":null]}>
		</span>
	);
})
"#
		.to_string(),
		transpile_jsx: false,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		is_server: Some(false),
		..TestInput::default()
	});
}

#[test]
fn example_optimization_issue_3795() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const Issue3795 = component$(() => {
	let base = "foo";
	const firstAssignment = base;
	base += "bar";
	const secondAssignment = base;
	return (
		<div id='issue-3795-result'>{firstAssignment} {secondAssignment}</div>
	)
	});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		transpile_jsx: true,
		is_server: Some(false),
		..TestInput::default()
	});
}

#[test]
fn example_drop_side_effects() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';
import { server$ } from '@qwik.dev/router';
import { clientSupabase } from 'supabase';
import { Client } from 'openai';
import { secret } from './secret';
import { sideEffect } from './secret';

const supabase = clientSupabase();
const dfd = new Client(secret);

(function() {
	console.log('run');
	})();
	(() => {
	console.log('run');
	})();

sideEffect();

export const api = server$(() => {
	supabase.from('ffg').do(dfd);
});

export default component$(() => {
	return (
		<button onClick$={() => await api()}></button>
	)
	});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Segment,
		strip_ctx_name: Some(vec!["server".into()]),
		transpile_ts: true,
		transpile_jsx: true,
		is_server: Some(false),
		mode: EmitMode::Dev,
		..TestInput::default()
	});
}

#[test]
fn example_reg_ctx_name_segments() {
	test_input!(TestInput {
		code: r#"
import { $, component$, server$ } from '@qwik.dev/core';
import { foo } from './foo';
export const Works = component$((props) => {
	const text = 'hola';
	return (
		<>
		<div onClick$={server$(() => console.log('in server', text))}></div>
		<div onClick$={() => foo()}></div>
		</>
	);
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Inline,
		reg_ctx_name: Some(vec!["server".into()]),
		strip_event_handlers: true,
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn reports_import_used_only_in_stripped_segment() {
	let output = test_input!(TestInput {
		code: r#"
import { component$, useVisibleTask$ } from '@qwik.dev/core';
import { ping } from '~/shared/visible-task-server-fn';

export const App = component$(() => {
	useVisibleTask$(async () => {
		await ping();
	});
	return <div>hi</div>;
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		strip_ctx_name: Some(vec!["useVisibleTask".into()]),
		strip_event_handlers: true,
		transpile_ts: true,
		transpile_jsx: true,
		is_server: Some(true),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();

	let root_module = output
		.modules
		.iter()
		.find(|module| !module.is_entry && module.segment.is_none())
		.expect("root module should exist");
	let visible_task_import: Atom = "~/shared/visible-task-server-fn".into();

	assert!(
		root_module.imports.contains(&visible_task_import),
		"root module should report imports used before stripping, got {:?}",
		root_module.imports
	);
}

#[test]
fn reports_dynamic_import_used_only_in_stripped_segment() {
	let output = test_input!(TestInput {
		code: r#"
import { component$, useVisibleTask$ } from '@qwik.dev/core';

export const App = component$(() => {
	useVisibleTask$(async () => {
		const { ping } = await import('~/shared/visible-task-server-fn');
		await ping();
	});
	return <div>hi</div>;
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		strip_ctx_name: Some(vec!["useVisibleTask".into()]),
		strip_event_handlers: true,
		transpile_ts: true,
		transpile_jsx: true,
		is_server: Some(true),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();

	let root_module = output
		.modules
		.iter()
		.find(|module| !module.is_entry && module.segment.is_none())
		.expect("root module should exist");
	let visible_task_import: Atom = "~/shared/visible-task-server-fn".into();

	assert!(
		root_module.imports.contains(&visible_task_import),
		"root module should report dynamic imports used before stripping, got {:?}",
		root_module.imports
	);
}

#[test]
fn example_reg_ctx_name_segments_inlined() {
	test_input!(TestInput {
		code: r#"
import { $, component$, server$ } from '@qwik.dev/core';
export const Works = component$((props) => {
	const text = 'hola';
	return (
		<div onClick$={server$(() => console.log('in server', text))}></div>
	);
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Inline,
		reg_ctx_name: Some(vec!["server".into()]),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_reg_ctx_name_segments_hoisted() {
	test_input!(TestInput {
		code: r#"
import { $, component$, server$, useStyle$ } from '@qwik.dev/core';

export const Works = component$((props) => {
	useStyle$(STYLES);
	const text = 'hola';
	return (
		<div onClick$={server$(() => console.log('in server', text))}></div>
	);
});

const STYLES = '.class {}';
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		reg_ctx_name: Some(vec!["server".into()]),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}
#[test]
fn example_lightweight_functional() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';

export const Foo = component$((props) => {
	return (
		<div>
			<Button {...props} />
			<ButtonArrow {...props} />
		</div>
	);
}, {
	tagName: "my-foo",
});

export function Button({text, color}) {
	return (
		<button onColor$={color} onClick$={()=>console.log(text, color)}>{text}</button>
	);
}

export const ButtonArrow = ({text, color}) => {
	return (
		<button onColor$={color} onClick$={()=>console.log(text, color)}>{text}</button>
	);
}
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_invalid_references() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';

const I1 = 12;
const [I2, {I3, v1: [I4], I5=v2, ...I6}, I7=v3, ...I8] = obj;
function I9() {}
class I10 {}

export const App = component$(({count}) => {
	console.log(I1, I2, I3, I4, I5, I6, I7, I8, I9);
	console.log(itsok, v1, v2, v3, obj);
	return $(() => {
		return (
			<I10></I10>
		)
	});
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_invalid_segment_expr1() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useStyles$ } from '@qwik.dev/core';
import css1 from './global.css';
import css2 from './style.css';

export const App = component$(() => {
	const style = `${css1}${css2}`;
	useStyles$(style);
	const render = () => {
		return (
			<div></div>
		)
	};
	return $(render);
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_capture_imports() {
	test_input!(TestInput {
		code: r#"
import { component$, useStyles$ } from '@qwik.dev/core';
import css1 from './global.css';
import css2 from './style.css';
import css3 from './style.css';

export const App = component$(() => {
	useStyles$(`${css1}${css2}`);
	useStyles$(css3);
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_capturing_fn_class() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';

export const App = component$(() => {
	function hola() {
		console.log('hola');
	}
	class Thing {}
	class Other {}

	return $(() => {
		hola();
		new Thing();
		return (
			<div></div>
		)
	});
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_renamed_exports() {
	test_input!(TestInput {
		code: r#"
import { component$ as Component, $ as onRender, useStore } from '@qwik.dev/core';

export const App = Component((props) => {
	const state = useStore({thing: 0});

	return onRender(() => (
		<div>{state.thing}</div>
	));
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_exports() {
	test_input!(TestInput {
		filename: "project/test.tsx".to_string(),
		code: r#"
import { $, component$ } from '@qwik.dev/core';

export const [a, {b, v1: [c], d=v2, ...e}, f=v3, ...g] = obj;

const exp1 = 1;
const internal = 2;
export {exp1, internal as expr2};

export function foo() { }
export class bar {}

export default function DefaultFn() {}

export const Header = component$(() => {
	return $(() => (
		<Footer>
			<div>{a}{b}{c}{d}{e}{f}{exp1}{internal}{foo}{bar}{DefaultFn}</div>
			<div>{v1}{v2}{v3}{obj}</div>
		</Footer>
	))
});

export const Footer = component$();
"#
		.to_string(),
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn issue_117() {
	test_input!(TestInput {
		filename: "project/test.tsx".to_string(),
		code: r#"
export const cache = patternCache[cacheKey] || (patternCache[cacheKey]={});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Single,
		..TestInput::default()
	});
}

#[test]
fn example_jsx() {
	test_input!(TestInput {
		code: r#"
import { $, component$, h, Fragment } from '@qwik.dev/core';

export const Lightweight = (props) => {
	return (
		<div>
			<>
				<div/>
				<button {...props}/>
			</>
		</div>
	)
};

export const Foo = component$((props) => {
	return $(() => {
		return (
			<div>
				<>
					<div class="class"/>
					<div class="class"></div>
					<div class="class">12</div>
				</>
				<div class="class">
					<Lightweight {...props}/>
				</div>
				<div class="class">
					<div/>
					<div/>
					<div/>
				</div>
				<div class="class">
					{children}
				</div>
			</div>
		)
	});
}, {
	tagName: "my-foo",
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_jsx_listeners() {
	test_input!(TestInput {
		code: r#"
import { $, component$ } from '@qwik.dev/core';

export const Foo = component$(() => {

	return $(() => {
		const handler = $(() => console.log('reused'));
		return (
			<div
				onClick$={()=>console.log('onClick$')}
				onDocumentScroll$={()=>console.log('onDocumentScroll')}
				onDocumentScroll$={()=>console.log('onWindowScroll')}

				on-cLick$={()=>console.log('on-cLick$')}
				onDocument-sCroll$={()=>console.log('onDocument-sCroll')}
				onDocument-scroLL$={()=>console.log('onDocument-scroLL')}

				host:onClick$={()=>console.log('host:onClick$')}
				host:onDocumentScroll$={()=>console.log('host:onDocument:scroll')}
				host:onDocumentScroll$={()=>console.log('host:onWindow:scroll')}

				onKeyup$={handler}
				onDocument:keyup$={handler}
				onWindow:keyup$={handler}

				custom$={()=>console.log('custom')}
			/>
		)
	});
}, {
	tagName: "my-foo",
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_qwik_conflict() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useStyles } from '@qwik.dev/core';
import { qrl } from '@qwik.dev/core/what';

export const hW = 12;
export const handleWatch = 42;

const componentQrl = () => console.log('not this', qrl());

componentQrl();
export const Foo = component$(() => {
	useStyles$('thing');
	const qwik = hW + handleWatch;
	console.log(qwik);
	const qrl = 23;
	return (
		<div onClick$={()=> console.log(qrl)}/>
	)
}, {
	tagName: "my-foo",
});

export const Root = component$(() => {
	useStyles($('thing'));
	return $(() => {
		return (
			<div/>
		)
	});
}, {
	tagName: "my-foo",
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_fix_dynamic_import() {
	test_input!(TestInput {
		filename: "project/folder/test.tsx".to_string(),
		code: r#"
import { $, component$ } from '@qwik.dev/core';
import thing from "../state";

export function foo() {
	return import("../foo/state2")
}

export const Header = component$(() => {
	return (
		<div>
			{import("../folder/state3")}
			{thing}
		</div>
	);
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Single,
		..TestInput::default()
	});
}

#[test]
fn example_custom_inlined_functions() {
	test_input!(TestInput {
		code: r#"
import { component$, $, useStore, wrap, useEffect } from '@qwik.dev/core';

export const useMemoQrl = (qrt) => {
	useEffect(qrt);
};

export const useMemo$ = wrap(useMemoQrl);

export const App = component$((props) => {
	const state = useStore({count: 0});
	useMemo$(() => {
		console.log(state.count);
	});
	return $(() => (
		<div>{state.count}</div>
	));
});

export const Lightweight = (props) => {
	useMemo$(() => {
		console.log(state.count);
	});
};
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_missing_custom_inlined_functions() {
	test_input!(TestInput {
		code: r#"
import { component$ as Component, $ as onRender, useStore, wrap, useEffect } from '@qwik.dev/core';


export const useMemo$ = (qrt) => {
	useEffect(qrt);
};

export const App = component$((props) => {
	const state = useStore({count: 0});
	useMemo$(() => {
		console.log(state.count);
	});
	return $(() => (
		<div>{state.count}</div>
	));
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_skip_transform() {
	test_input!(TestInput {
		code: r#"
import { component$ as Component, $ as onRender } from '@qwik.dev/core';

export const handler = $(()=>console.log('hola'));

export const App = component$((props) => {
	useStyles$('hola');
	return $(() => (
		<div>{state.thing}</div>
	));
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_explicit_ext_transpile() {
	test_input!(TestInput {
		code: r#"
import { component$, $, useStyles$ } from '@qwik.dev/core';

export const App = component$((props) => {
	useStyles$('hola');
	return $(() => (
		<div></div>
	));
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_explicit_ext_no_transpile() {
	test_input!(TestInput {
		code: r#"
import { component$, $, useStyles$ } from '@qwik.dev/core';

export const App = component$((props) => {
	useStyles$('hola');
	return $(() => (
		<div></div>
	));
});
"#
		.to_string(),
		explicit_extensions: true,
		entry_strategy: EntryStrategy::Single,
		..TestInput::default()
	});
}

#[test]
fn example_jsx_import_source() {
	test_input!(TestInput {
		code: r#"
/* @jsxImportSource react */

import { qwikify$ } from './qwikfy';

export const App = () => (
	<div onClick$={()=>console.log('App')}></div>
);

export const App2 = qwikify$(() => (
	<div onClick$={()=>console.log('App2')}></div>
));
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_prod_node() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const Foo = component$(() => {
	return (
		<div>
			<div onClick$={() => console.log('first')}/>
			<div onClick$={() => console.log('second')}/>
			<div onClick$={() => console.log('third')}/>
		</div>
	);
});
"#
		.to_string(),
		mode: EmitMode::Prod,
		..TestInput::default()
	});
}

#[test]
fn example_use_client_effect() {
	test_input!(TestInput {
		code: r#"
import { component$, useBrowserVisibleTask$, useStore, useStyles$ } from '@qwik.dev/core';

export const Child = component$(() => {
	const state = useStore({
		count: 0
	});

	// Double count watch
	useBrowserVisibleTask$(() => {
		const timer = setInterval(() => {
		state.count++;
		}, 1000);
		return () => {
		clearInterval(timer);
		}
	});

	return (
		<div>
		{state.count}
	</div>
	);
});

"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_inlined_entry_strategy() {
	test_input!(TestInput {
		code: r#"
import { component$, useBrowserVisibleTask$, useStore, useStyles$ } from '@qwik.dev/core';
import { thing } from './sibling';
import mongodb from 'mongodb';

export const Child = component$(() => {

	useStyles$('somestring');
	const state = useStore({
		count: 0
	});

	// Double count watch
	useBrowserVisibleTask$(() => {
		state.count = thing.doStuff() + import("./sibling");
	});

	return (
		<div onClick$={() => console.log(mongodb)}>
		</div>
	);
});

"#
		.to_string(),
		entry_strategy: EntryStrategy::Inline,
		..TestInput::default()
	});
}

#[test]
fn example_default_export() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';
import { sibling } from './sibling';

export default component$(() => {
	return (
		<div onClick$={() => console.log(mongodb, sibling)}>
		</div>
	);
});

"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		filename: "src/routes/_repl/[id]/[[...slug]].tsx".into(),
		entry_strategy: EntryStrategy::Smart,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_default_export_index() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export default component$(() => {
	return (
		<div onClick$={() => console.log(mongodb)}>
		</div>
	);
});

"#
		.to_string(),
		filename: "src/components/mongo/index.tsx".into(),
		entry_strategy: EntryStrategy::Inline,
		..TestInput::default()
	});
}

#[test]
fn example_default_export_invalid_ident() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export default component$(() => {
	return (
		<div onClick$={() => console.log(mongodb)}>
		</div>
	);
});

"#
		.to_string(),
		filename: "src/components/mongo/404.tsx".into(),
		..TestInput::default()
	});
}

#[test]
fn example_parsed_inlined_qrls() {
	test_input!(TestInput {
		code: r#"
import { componentQrl, inlinedQrl, useStore, jsxs, jsx, useLexicalScope } from '@qwik.dev/core';

export const App = /*#__PURE__*/ componentQrl(inlinedQrl(()=>{
	useStyles$(inlinedQrl(STYLES, "STYLES_odz7dfdfdM"));
	useStyles$(inlinedQrl(STYLES, "STYLES_odzdfdfdM"));

	const store = useStore({
		count: 0
	});
	return /*#__PURE__*/ jsxs("div", {
		children: [
			/*#__PURE__*/ jsxs("p", {
				children: [
					"Count: ",
					store.count
				]
			}),
			/*#__PURE__*/ jsx("p", {
				children: /*#__PURE__*/ jsx("button", {
					onClick$: inlinedQrl(()=>{
						const [store] = useLexicalScope();
						return store.count++;
					}, "App_component_div_p_button_onClick_odz7eidI4GM", [
						store
					]),
					children: "Click"
				})
			})
		]
	});
}, "App_component_Fh88JClhbC0"));

export const STYLES = ".red { color: red; }";

"#
		.to_string(),
		entry_strategy: EntryStrategy::Inline,
		mode: EmitMode::Prod,
		transpile_ts: false,
		..TestInput::default()
	});
}

#[test]
fn example_use_server_mount() {
	test_input!(TestInput {
		code: r#"
import { component$, useTask$, useStore, useStyles$ } from '@qwik.dev/core';
import mongo from 'mongodb';
import redis from 'redis';

export const Parent = component$(() => {
	const state = useStore({
		text: ''
	});

	// Double count watch
	useTask$(async () => {
		state.text = await mongo.users();
		redis.set(state.text);
	});

	return (
		<div onClick$={() => console.log('parent')}>
			{state.text}
		</div>
	);
});

export const Child = component$(() => {
	const state = useStore({
		text: ''
	});

	// Double count watch
	useTask$(async () => {
		state.text = await mongo.users();
	});

	return (
		<div onClick$={() => console.log('child')}>
			{state.text}
		</div>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Smart,
		..TestInput::default()
	});
}

#[test]
fn example_manual_chunks() {
	test_input!(TestInput {
		code: r#"
import { component$, useTask$, useStore, useStyles$ } from '@qwik.dev/core';
import mongo from 'mongodb';
import redis from 'redis';

export const Parent = component$(() => {
	const state = useStore({
		text: ''
	});

	// Double count watch
	useTask$(async () => {
		state.text = await mongo.users();
		redis.set(state.text);
	});

	return (
		<div onClick$={() => console.log('parent')}>
			{state.text}
		</div>
	);
});

export const Child = component$(() => {
	const state = useStore({
		text: ''
	});

	// Double count watch
	useTask$(async () => {
		state.text = await mongo.users();
	});

	return (
		<div onClick$={() => console.log('child')}>
			{state.text}
		</div>
	);
});
"#
		.to_string(),
		// filler to maintain line offsets
		// this is a test for manual chunks
		// which are no longer used in the optimizer
		//
		transpile_ts: true,
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Smart,
		..TestInput::default()
	});
}

#[test]
fn example_strip_exports_unused() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';
import mongodb from 'mongodb';

export const onGet = () => {
	const data = mongodb.collection.whatever;
	return {
		body: {
		data
		}
	}
};

export default component$(()=> {
	return <div>cmp</div>
});
"#
		.to_string(),
		strip_exports: Some(vec!["onGet".into()]),
		..TestInput::default()
	});
}

#[test]
fn example_strip_exports_used() {
	test_input!(TestInput {
		code: r#"
import { component$, useResource$ } from '@qwik.dev/core';
import mongodb from 'mongodb';

export const onGet = () => {
	const data = mongodb.collection.whatever;
	return {
		body: {
		data
		}
	}
};

export default component$(()=> {
	useResource$(() => {
		return onGet();
	})
	return <div>cmp</div>
});
"#
		.to_string(),
		strip_exports: Some(vec!["onGet".into()]),
		..TestInput::default()
	});
}

#[test]
fn example_strip_server_code() {
	test_input!(TestInput {
		code: r#"
import { component$, serverLoader$, serverStuff$, $, client$, useStore, useTask$ } from '@qwik.dev/core';
import { isServer } from '@qwik.dev/core';
import mongo from 'mongodb';
import redis from 'redis';
import { handler } from 'serverless';

export const Parent = component$(() => {
	const state = useStore({
		text: ''
	});

	// Double count watch
	useTask$(async () => {
		if (!isServer) return;
		state.text = await mongo.users();
		redis.set(state.text);
	});

	serverStuff$(async () => {
		// should be removed too
		const a = $(() => {
			// from $(), should not be removed
		});
		const b = client$(() => {
			// from clien$(), should not be removed
		});
		return [a,b];
	})

	serverLoader$(handler);

	useTask$(() => {
		// Code
	});

	return (
		<div onClick$={() => console.log('parent')}>
			{state.text}
		</div>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Segment,
		strip_ctx_name: Some(vec!["server".into()]),
		mode: EmitMode::Prod,
		..TestInput::default()
	});
}

#[test]
fn example_server_auth() {
	test_input!(TestInput {
		code: r#"
import GitHub from '@auth/core/providers/github'
import Facebook from 'next-auth/providers/facebook'
import Google from 'next-auth/providers/google'
import {serverAuth$, auth$} from '@auth/qwik';

export const { onRequest, logout, getSession, signup } = serverAuth$({
	providers: [
	GitHub({
		clientId: process.env.GITHUB_ID,
		clientSecret: process.env.GITHUB_SECRET
	}),
	Facebook({
		clientId: import.meta.env.FACEBOOK_ID,
		clientSecret: import.meta.env.FACEBOOK_SECRET
	}),
	Google({
		clientId: process.env.GOOGLE_ID,
		clientSecret: process.env.GOOGLE_SECRET
	})
	]
});

export const { onRequest, logout, getSession, signup } = auth$({
	providers: [
	GitHub({
		clientId: process.env.GITHUB_ID,
		clientSecret: process.env.GITHUB_SECRET
	}),
	Facebook({
		clientId: process.env.FACEBOOK_ID,
		clientSecret: process.env.FACEBOOK_SECRET
	}),
	Google({
		clientId: process.env.GOOGLE_ID,
		clientSecret: process.env.GOOGLE_SECRET
	})
	]
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Segment,
		..TestInput::default()
	});
}

#[test]
fn example_strip_client_code() {
	test_input!(TestInput {
		code: r#"
import { component$, useClientMount$, useStore, useTask$ } from '@qwik.dev/core';
import mongo from 'mongodb';
import redis from 'redis';
import threejs from 'threejs';
import { a } from './keep';
import { b } from '../keep2';
import { c } from '../../remove';

export const Parent = component$(() => {
	const state = useStore({
		text: ''
	});

	// Double count watch
	useClientMount$(async () => {
		state.text = await mongo.users();
		redis.set(state.text, a, b, c);
	});

	useTask$(() => {
		// Code
	});

	return (
		<div
			shouldRemove$={() => state.text}
			onClick$={() => console.log('parent', state, threejs)}
		>
			<Div
				onClick$={() => console.log('keep')}
				render$={() => state.text}
			/>
			{state.text}
		</div>
	);
});
"#
		.to_string(),
		filename: "components/component.tsx".into(),
		transpile_ts: true,
		transpile_jsx: true,
		entry_strategy: EntryStrategy::Inline,
		strip_ctx_name: Some(vec!["useClientMount$".into()]),
		strip_event_handlers: true,
		..TestInput::default()
	});
}

#[test]
fn issue_150() {
	test_input!(TestInput {
		code: r#"
import { component$, $ } from '@qwik.dev/core';
import { hola } from 'sdfds';

export const Greeter = component$(() => {
	const stuff = useStore();
	return $(() => {
		return (
			<div
				class={{
					'foo': true,
					'bar': stuff.condition,
					'baz': hola ? 'true' : 'false',
				}}
			/>
		)
	});
});

const d = $(()=>console.log('thing'));
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_input_bind() {
	test_input!(TestInput {
		code: r#"
import { component$, $ } from '@qwik.dev/core';

export const Greeter = component$(() => {
	const value = useSignal(0);
	const checked = useSignal(false);
	const stuff = useSignal();
	return (
		<>
			<input bind:value={value} />
			<input bind:checked={checked} />
			<input bind:stuff={stuff} />
			<div>{value}</div>
			<div>{value.value}</div>
		</>

	)
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		transpile_jsx: true,
		mode: EmitMode::Prod,
		..TestInput::default()
	});
}

#[test]
fn example_import_assertion() {
	test_input!(TestInput {
		code: r#"
import { component$, $ } from '@qwik.dev/core';
import json from "./foo.json" assert { type: "json" };

export const Greeter = component$(() => {
	return json;
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn support_windows_paths() {
	let res = test_input!(TestInput {
		filename: r"components\apps\apps.tsx".to_string(),
		src_dir: r"C:\users\apps".to_string(),
		code: r#"
import { component$ } from '@qwik.dev/core';
export const Greeter = component$(() => <div/>)
"#
		.to_string(),
		transpile_jsx: true,
		is_server: Some(false),
		entry_strategy: EntryStrategy::Segment,
		..TestInput::default()
	})
	.unwrap();
	// verify that none of the modules have a path that contains backslashes
	for module in res.modules {
		assert!(!module.path.contains('\\'));
	}
}
// filler to retain assertion line numbers
//
//
//
//
//

#[test]
fn issue_476() {
	test_input!(TestInput {
		code: r#"
import { Counter } from "./counter.tsx";

export const Root = () => {
	return (
		<html>
			<head>
				<meta charset="utf-8" />
				<title>Qwik Blank App</title>
			</head>
			<body>
				<Counter initial={3} />
			</body>
		</html>
	);
};
"#
		.to_string(),
		transpile_ts: false,
		transpile_jsx: false,
		..TestInput::default()
	});
}

#[test]
fn issue_964() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const App = component$(() => {
	console.log(function*(lo: any, t: any) {
	console.log(yield (yield lo)(t.href).then((r) => r.json()));
	});

	return <p>Hello Qwik</p>;
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_immutable_analysis() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, $ } from '@qwik.dev/core';
import importedValue from 'v';
import styles from './styles.module.css';

export const App = component$((props) => {
	const {Model} = props;
	const state = useStore({count: 0});
	const remove = $((id: number) => {
		const d = state.data;
		d.splice(
			d.findIndex((d) => d.id === id),
			1
		)
		});
	return (
		<>
			<p class="stuff" onClick$={props.onClick$}>Hello Qwik</p>
			<Div
				class={styles.foo}
				document={window.document}
				onClick$={props.onClick$}
				onEvent$={() => console.log('stuff')}
				transparent$={() => {console.log('stuff')}}
				immutable1="stuff"
				immutable2={{
					foo: 'bar',
					baz: importedValue ? true : false,
				}}
				immutable3={2}
				immutable4$={(ev) => console.log(state.count)}
				immutable5={[1, 2, importedValue, null, {}]}
			>
				<p>Hello Qwik</p>
			</Div>
			[].map(() => (
				<Model
					class={state}
					remove$={remove}
					mutable1={{
						foo: 'bar',
						baz: state.count ? true : false,
					}}
					mutable2={(() => console.log(state.count))()}
					mutable3={[1, 2, state, null, {}]}
				/>
			));
		</>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_ts_enums_issue_1341() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

enum Thing {
	A,
	B
}

export const App = component$(() => {
	console.log(Thing.A);
	return (
		<>
			<p class="stuff">Hello Qwik</p>
		</>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_ts_enums_no_transpile() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export enum Thing {
	A,
	B
}

export const App = component$(() => {
	console.log(Thing.A);
	return (
		<>
			<p class="stuff">Hello Qwik</p>
		</>
	);
});
"#
		.to_string(),
		transpile_ts: false,
		transpile_jsx: false,

		..TestInput::default()
	});
}

#[test]
fn example_ts_enums() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export enum Thing {
	A,
	B
}

export const App = component$(() => {
	console.log(Thing.A);
	return (
		<>
			<p class="stuff">Hello Qwik</p>
		</>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn special_jsx() {
	test_input!(TestInput {
		code: r#"
// don't transpile jsx with non-plain-object props
import { jsx } from '@qwik.dev/core';

export const App = () => {
    const props = {}
    return jsx('div', props, 'Hello Qwik');
}
"#
		.to_string(),
		transpile_ts: false,
		transpile_jsx: false,
		..TestInput::default()
	});
}

#[test]
fn example_dev_mode() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$(() => {
	return (
		<Cmp>
			<p class="stuff" onClick$={() => console.log('warn')}>Hello Qwik</p>
		</Cmp>
	);
});
"#
		.to_string(),
		mode: EmitMode::Dev,
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_dev_mode_inlined() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$(() => {
	return (
		<Cmp>
			<p class="stuff" onClick$={() => console.log('warn')}>Hello Qwik</p>
		</Cmp>
	);
});
"#
		.to_string(),
		mode: EmitMode::Dev,
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_transpile_jsx_only() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$((props) => {
	return (
		<Cmp>
			<p class="stuff" onClick$={() => console.log('warn')}>Hello Qwik</p>
		</Cmp>
	);
});
"#
		.to_string(),
		transpile_ts: false,
		transpile_jsx: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_spread_jsx() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';
import { useDocumentHead, useLocation } from '@qwik.dev/router';

/**
 * The RouterHead component is placed inside of the document `<head>` element.
 */
export const RouterHead = component$(() => {
	const head = useDocumentHead();
	const loc = useLocation();

	return (
	<>
		<title>{head.title}</title>

		<link rel="canonical" href={loc.href} />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />

		{head.meta.map((m) => (
			<meta {...m} />
		))}

		{head.links.map((l) => (
			<link {...l} key={l.key} />
		))}

		{head.styles.map((s) => (
			<style {...s.props} dangerouslySetInnerHTML={s.style} key={s.key} />
		))}
	</>
	);
});"#
			.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_export_issue() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

const App = component$(() => {
	return (
		<div>hola</div>
	);
});


export const Root = component$((props: Stuff) => {
	return (
		<App/>
	);
});

const Other = 12;
export { Other as App };

export default App;
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_jsx_keyed() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$((props: Stuff) => {
	return (
		<>
			<Cmp key="stuff"></Cmp>
			<Cmp></Cmp>
			<Cmp prop="23"></Cmp>
			<Cmp prop="23" key={props.stuff}></Cmp>
			<p key={props.stuff}>Hello Qwik</p>
		</>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_jsx_keyed_dev() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$((props: Stuff) => {
	return (
		<>
			<Cmp key="stuff"></Cmp>
			<Cmp></Cmp>
			<Cmp prop="23"></Cmp>
			<Cmp prop="23" key={props.stuff}></Cmp>
			<p key={props.stuff}>Hello Qwik</p>
		</>
	);
});
"#
		.to_string(),
		filename: "project/index.tsx".into(),
		src_dir: "/src/project".into(),
		transpile_ts: true,
		transpile_jsx: true,
		mode: EmitMode::Dev,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_mutable_children() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, Slot, Fragment } from '@qwik.dev/core';
import Image from './image.jpg?jsx';

export function Fn1(props: Stuff) {
	return (
		<>
			<div>{prop < 2 ? <p>1</p> : <Stuff>2</Stuff>}</div>
		</>
	);
}

export function Fn2(props: Stuff) {
	return (
		<div>{prop.value && <Stuff></Stuff>}<div></div></div>
	);
}

export function Fn3(props: Stuff) {
	if (prop.value) {
		return (
			<Stuff></Stuff>
		);
	}
	return (
		<div></div>
	);
}

export function Fn4(props: Stuff) {
	if (prop.value) {
		return (
			<div></div>
		);
	}
	return (
		<Stuff></Stuff>
	);
}

export const Arrow = (props: Stuff) => <div>{prop < 2 ? <p>1</p> : <Stuff>2</Stuff>}</div>;

export const AppDynamic1 = component$((props: Stuff) => {
	return (
		<>
			<div>{prop < 2 ? <p>1</p> : <Stuff>2</Stuff>}</div>
		</>
	);
});
export const AppDynamic2 = component$((props: Stuff) => {
	return (
		<div>{prop.value && <Stuff></Stuff>}<div></div></div>
	);
});

export const AppDynamic3 = component$((props: Stuff) => {
	if (prop.value) {
		return (
			<Stuff></Stuff>
		);
	}
	return (
		<div></div>
	);
});

export const AppDynamic4 = component$((props: Stuff) => {
	if (prop.value) {
		return (
			<div></div>
		);
	}
	return (
		<Stuff></Stuff>
	);
});

export const AppStatic = component$((props: Stuff) => {
	return (
		<>
			<div>Static {f ? 1 : 3}</div>
			<div>{prop < 2 ? <p>1</p> : <p>2</p>}</div>

			<div>{prop.value && <div></div>}</div>
			<div>{prop.value && <Fragment><Slot></Slot></Fragment>}</div>
			<div>{prop.value && <><div></div></>}</div>
			<div>{prop.value && <Image/>}</div>
			<div>Static {f ? 1 : 3}</div>
			<div>Static</div>
			<div>Static {props.value}</div>
			<div>Static {stuff()}</div>
			<div>Static {stuff()}</div>
		</>
	);
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		transpile_ts: true,
		transpile_jsx: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_immutable_function_components() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, Slot } from '@qwik.dev/core';

export const App = component$((props: Stuff) => {
	return (
		<div>
			<Slot/>
		</div>
	);
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		transpile_ts: true,
		transpile_jsx: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}
#[test]
fn example_transpile_ts_only() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$((props: Stuff) => {
	return (
		<Cmp>
			<p class="stuff" onClick$={() => console.log('warn')}>Hello Qwik</p>
		</Cmp>
	);
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: true,
		transpile_jsx: false,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_class_name() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const App2 = component$(() => {
	const signal = useSignal();
	const computed = signal.value + 'foo';
	return (
		<>
			<div className="hola"></div>
			<div className={signal.value}></div>
			<div className={signal}></div>
			<div className={computed}></div>

			<Foo className="hola"></Foo>
			<Foo className={signal.value}></Foo>
			<Foo className={signal}></Foo>
			<Foo className={computed}></Foo>
		</>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_preserve_filenames() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$((props) => {
	return (
		<Cmp>
			<p class="stuff" onClick$={() => console.log('warn')}>Hello Qwik</p>
		</Cmp>
	);
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Inline,
		transpile_ts: false,
		transpile_jsx: true,
		preserve_filenames: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_preserve_filenames_segments() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$((props: Stuff) => {
	foo();
	return (
		<Cmp>
			<p class="stuff" onClick$={() => console.log('warn')}>Hello Qwik</p>
		</Cmp>
	);
});

export const foo = () => console.log('foo');
"#
		.to_string(),
		entry_strategy: EntryStrategy::Segment,
		transpile_ts: true,
		transpile_jsx: true,
		preserve_filenames: true,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_build_server() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, isDev, isServer as isServer2 } from '@qwik.dev/core';
import { isServer, isBrowser as isb } from '@qwik.dev/core/build';
import { mongodb } from 'mondodb';
import { threejs } from 'threejs';

import L from 'leaflet';

export const functionThatNeedsWindow = () => {
  if (isb) {
    console.log('l', L);
    console.log('hey');
    window.alert('hey');
  }
};

export const App = component$(() => {
	useMount$(() => {
		if (isServer) {
			console.log('server', mongodb());
		}
		if (isb) {
			console.log('browser', new threejs());
		}
	});
	return (
		<Cmp>
			{isServer2 && <p>server</p>}
			{isb && <p>server</p>}
		</Cmp>
	);
});
"#
		.to_string(),
		is_server: Some(true),
		mode: EmitMode::Prod,
		..TestInput::default()
	});
}

#[test]
fn example_derived_signals_div() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, mutable } from '@qwik.dev/core';

import {dep} from './file';
import styles from './styles.module.css';

export const App = component$((props) => {
	const signal = useSignal(0);
	const store = useStore({});
	const count = props.counter.count;

	return (
		<div
			class={{
				even: count % 2 === 0,
				odd: count % 2 === 1,
				stable0: true,
				hidden: false,
			}}
			staticClass={styles.foo}
			staticDocument={window.document}
			staticText="text"
			staticText2={`text`}
			staticNumber={1}
			staticBoolean={true}
			staticExpr={`text${12}`}
			staticExpr2={typeof `text${12}` === 'string' ? 12 : 43}

			signal={signal}
			signalValue={signal.value}
			signalComputedValue={12 + signal.value}

			store={store.address.city.name}
			storeComputed={store.address.city.name ? 'true' : 'false'}

			dep={dep}
			depAccess={dep.thing}
			depComputed={dep.thing + 'stuff'}

			global={globalThing}
			globalAccess={globalThing.thing}
			globalComputed={globalThing.thing + 'stuff'}


			noInline={signal.value()}
			noInline2={signal.value + unknown()}
			noInline3={mutable(signal)}
			noInline4={signal.value + dep}
		/>

	);
});
"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
}

#[test]
fn example_issue_4438() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export const App = component$(() => {
	const toggle = useSignal(false);
	return (
		<>
			<div data-nu={toggle.value ? $localize`singular` : 'plural'}></div>
			<div>{toggle.value ? $localize`singular` : $localize`plural`}</div>
		</>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
}

#[test]
fn example_derived_signals_children() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, mutable } from '@qwik.dev/core';

import {dep} from './file';

export const TextContent = component$((props) => {
	return (
		<>
			<div>data-nu: {props['data-nu']}</div>
			<div>class: {props.class}</div>
		</>
	);
});

export const App = component$(() => {
	const signal = useSignal(0);
	const store = useStore({});
	return (
		<>
			<div>text</div>
			<div>{`text`}</div>
			<div>{1}</div>
			<div>{true}</div>
			<div>{`text${12}`}</div>
			<div>{typeof `text${12}` === 'string' ? 12 : 43}</div>
			<div>{signal}</div>
			<div>{signal.value}</div>
			<div>{12 + signal.value}</div>
			<div>{store.address.city.name}</div>
			<div>{store.address.city.name ? 'true' : 'false'}</div>
			<div>{dep}</div>
			<div>{dep.thing}</div>
			<div>{dep.thing + 'stuff'}</div>
			<div>{globalThing}</div>
			<div>{globalThing.thing}</div>
			<div>{globalThing.thing + 'stuff'}</div>
			<div>{signal.value()}</div>
			<div>{signal.value + unknown()}</div>
			<div>{mutable(signal)}</div>
			<div>{signal.value + dep}</div>
		</>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
}

#[test]
fn example_derived_signals_multiple_children() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, mutable } from '@qwik.dev/core';

import {dep} from './file';

export const App = component$(() => {
	const signal = useSignal(0);
	const store = useStore({});
	return (
		<>
			<div>First text</div>
			<div>First {`text`}</div>
			<div>First {1}</div>
			<div>First {true}</div>
			<div>First {`text${12}`}</div>
			<div>First {typeof `text${12}` === 'string' ? 12 : 43}</div>
			<div>First {signal}</div>
			<div>First {signal.value}</div>
			<div>First {12 + signal.value}</div>
			<div>First {store.address.city.name}</div>
			<div>First {store.address.city.name ? 'true' : 'false'}</div>
			<div>First {dep}</div>
			<div>First {dep.thing}</div>
			<div>First {dep.thing + 'stuff'}</div>
			<div>First {globalThing}</div>
			<div>First {globalThing.thing}</div>
			<div>First {globalThing.thing + 'stuff'}</div>
			<div>First {signal.value()}</div>
			<div>First {signal.value + unknown()}</div>
			<div>First {mutable(signal)}</div>
			<div>First {signal.value + dep}</div>
		</>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
}

#[test]
fn example_derived_signals_complext_children() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, mutable } from '@qwik.dev/core';

import {dep} from './file';

export const App = component$(() => {
	const signal = useSignal(0);
	const store = useStore({});
	return (
		<>
			<ul id="issue-2800-result">
				{Object.entries(store).map(([key, value]) => (
				<li>
					{key} - {value}
				</li>
				))}
			</ul>
		</>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
}

#[test]
fn example_derived_signals_cmp() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, mutable } from '@qwik.dev/core';

import {dep} from './file';
import {Cmp} from './cmp';

export const App = component$(() => {
	const signal = useSignal(0);
	const store = useStore({});
	return (
		<Cmp
			staticText="text"
			staticText2={`text`}
			staticNumber={1}
			staticBoolean={true}
			staticExpr={`text${12}`}
			staticExpr2={typeof `text${12}` === 'string' ? 12 : 43}

			signal={signal}
			signalValue={signal.value}
			signalComputedValue={12 + signal.value}

			store={store.address.city.name}
			storeComputed={store.address.city.name ? 'true' : 'false'}

			dep={dep}
			depAccess={dep.thing}
			depComputed={dep.thing + 'stuff'}

			global={globalThing}
			globalAccess={globalThing.thing}
			globalComputed={globalThing.thing + 'stuff'}


			noInline={signal.value()}
			noInline2={signal.value + unknown()}
			noInline3={mutable(signal)}
			noInline4={signal.value + dep}
		/>
	);
});
"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
}

#[test]
fn example_issue_33443() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export const Issue3742 = component$(({description = '', other}: any) => {
	const counter = useSignal(0);
	return (
		<div
		title={(description && 'description' in other) ? `Hello ${counter.value}` : `Bye ${counter.value}`}
		>
		Issue3742
		<button onClick$={() => counter.value++}>
			Increment
		</button>
		</div>
	)
	});
	"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
}
#[test]
fn example_getter_generation() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore } from '@qwik.dev/core';

export const App = component$(() => {
	const store = useStore({
		count: 0,
		stuff: 0,
		nested: {
			count: 0
		}
	});
	const signal = useSignal(0);
	return (
		<Cmp
			prop={'true' + 1 ? 'true' : ''}
			count={store.count}
			nested={store.nested.count}
			signal={signal}
			store={store.stuff + 12}
			value={signal.formData?.get('username')}
		>
		</Cmp>
	);
});

export const Cmp = component$((props) => {
	return (
		<>
			<p data-value={props.count}>{props.nested.count}</p>
			<p>Value {props.count}<span></span></p>
		</>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_qwik_react() {
	test_input!(TestInput {
		code: r#"
import { componentQrl, inlinedQrl, useLexicalScope, useHostElement, useStore, useTaskQrl, noSerialize, SkipRerender, implicit$FirstArg } from '@qwik.dev/core';
import { jsx, Fragment } from '@qwik.dev/core/jsx-runtime';
import { isBrowser, isServer } from '@qwik.dev/core';

function qwikifyQrl(reactCmpQrl) {
	return /*#__PURE__*/ componentQrl(inlinedQrl((props)=>{
		const [reactCmpQrl] = useLexicalScope();
		const hostElement = useHostElement();
		const store = useStore({});
		let run;
		if (props['client:visible']) run = 'visible';
		else if (props['client:load'] || props['client:only']) run = 'load';
		useTaskQrl(inlinedQrl(async (track)=>{
			const [hostElement, props, reactCmpQrl, store] = useLexicalScope();
			track(props);
			if (isBrowser) {
				if (store.data) store.data.root.render(store.data.client.Main(store.data.cmp, filterProps(props)));
				else {
					const [Cmp, client] = await Promise.all([
						reactCmpQrl.resolve(),
						import('./client-f762f78c.js')
					]);
					let root;
					if (hostElement.childElementCount > 0) root = client.hydrateRoot(hostElement, client.Main(Cmp, filterProps(props), store.event));
					else {
						root = client.createRoot(hostElement);
						root.render(client.Main(Cmp, filterProps(props)));
					}
					store.data = noSerialize({
						client,
						cmp: Cmp,
						root
					});
				}
			}
		}, "qwikifyQrl_component_useWatch_x04JC5xeP1U", [
			hostElement,
			props,
			reactCmpQrl,
			store
		]), {
			run
		});
		if (isServer && !props['client:only']) {
			const jsx$1 = Promise.all([
				reactCmpQrl.resolve(),
				import('./server-9ac6caad.js')
			]).then(([Cmp, server])=>{
				const html = server.render(Cmp, filterProps(props));
				return /*#__PURE__*/ jsx(Host, {
					dangerouslySetInnerHTML: html,
					[_IMMUTABLE]: [
						"dangerouslySetInnerHTML"
					]
				});
			});
			return /*#__PURE__*/ jsx(Fragment, {
				children: jsx$1
			});
		}
		return /*#__PURE__*/ jsx(Host, {
			children: /*#__PURE__*/ jsx(SkipRerender, {})
		});
	}, "qwikifyQrl_component_zH94hIe0Ick", [
		reactCmpQrl
	]), {
		tagName: 'qwik-wrap'
	});
}
const filterProps = (props)=>{
	const obj = {};
	Object.keys(props).forEach((key)=>{
		if (!key.startsWith('client:')) obj[key] = props[key];
	});
	return obj;
};
const qwikify$ = implicit$FirstArg(qwikifyQrl);

async function renderToString(rootNode, opts) {
	const mod = await import('./server-9ac6caad.js');
	const result = await mod.renderToString(rootNode, opts);
	const styles = mod.getGlobalStyleTag(result.html);
	const finalHtml = styles + result.html;
	return {
		...result,
		html: finalHtml
	};
}

export { qwikify$, qwikifyQrl, renderToString };
		"#
		.to_string(),
		filename: "../node_modules/@qwik.dev/react/index.qwik.mjs".to_string(),
		entry_strategy: EntryStrategy::Segment,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_qwik_react_inline() {
	test_input!(TestInput {
		code: r#"
import { componentQrl, inlinedQrl, useLexicalScope, useHostElement, useStore, useTaskQrl, noSerialize, SkipRerender, implicit$FirstArg } from '@qwik.dev/core';
import { jsx, Fragment } from '@qwik.dev/core/jsx-runtime';
import { isBrowser, isServer } from '@qwik.dev/core';

function qwikifyQrl(reactCmpQrl) {
	return /*#__PURE__*/ componentQrl(inlinedQrl((props)=>{
		const [reactCmpQrl] = useLexicalScope();
		const hostElement = useHostElement();
		const store = useStore({});
		let run;
		if (props['client:visible']) run = 'visible';
		else if (props['client:load'] || props['client:only']) run = 'load';
		useTaskQrl(inlinedQrl(async (track)=>{
			const [hostElement, props, reactCmpQrl, store] = useLexicalScope();
			track(props);
			if (isBrowser) {
				if (store.data) store.data.root.render(store.data.client.Main(store.data.cmp, filterProps(props)));
				else {
					const [Cmp, client] = await Promise.all([
						reactCmpQrl.resolve(),
						import('./client-f762f78c.js')
					]);
					let root;
					if (hostElement.childElementCount > 0) root = client.hydrateRoot(hostElement, client.Main(Cmp, filterProps(props), store.event));
					else {
						root = client.createRoot(hostElement);
						root.render(client.Main(Cmp, filterProps(props)));
					}
					store.data = noSerialize({
						client,
						cmp: Cmp,
						root
					});
				}
			}
		}, "qwikifyQrl_component_useWatch_x04JC5xeP1U", [
			hostElement,
			props,
			reactCmpQrl,
			store
		]), {
			run
		});
		if (isServer && !props['client:only']) {
			const jsx$1 = Promise.all([
				reactCmpQrl.resolve(),
				import('./server-9ac6caad.js')
			]).then(([Cmp, server])=>{
				const html = server.render(Cmp, filterProps(props));
				return /*#__PURE__*/ jsx(Host, {
					dangerouslySetInnerHTML: html,
					[_IMMUTABLE]: [
						"dangerouslySetInnerHTML"
					]
				});
			});
			return /*#__PURE__*/ jsx(Fragment, {
				children: jsx$1
			});
		}
		return /*#__PURE__*/ jsx(Host, {
			children: /*#__PURE__*/ jsx(SkipRerender, {})
		});
	}, "qwikifyQrl_component_zH94hIe0Ick", [
		reactCmpQrl
	]), {
		tagName: 'qwik-wrap'
	});
}
const filterProps = (props)=>{
	const obj = {};
	Object.keys(props).forEach((key)=>{
		if (!key.startsWith('client:')) obj[key] = props[key];
	});
	return obj;
};
const qwikify$ = implicit$FirstArg(qwikifyQrl);

async function renderToString(rootNode, opts) {
	const mod = await import('./server-9ac6caad.js');
	const result = await mod.renderToString(rootNode, opts);
	const styles = mod.getGlobalStyleTag(result.html);
	const finalHtml = styles + result.html;
	return {
		...result,
		html: finalHtml
	};
}

export { qwikify$, qwikifyQrl, renderToString };
		"#
		.to_string(),
		filename: "../node_modules/@qwik.dev/react/index.qwik.mjs".to_string(),
		entry_strategy: EntryStrategy::Inline,
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn example_qwik_router_client() {
	test_input!(TestInput {
		code: include_str!("fixtures/index.qwik.mjs").to_string(),
		filename: "../node_modules/@qwik.dev/router/index.qwik.mjs".to_string(),
		explicit_extensions: true,
		..TestInput::default()
	});
}

#[test]
fn relative_paths() {
	let dep = r#"
import { componentQrl, inlinedQrl, useStore, useLexicalScope } from "@qwik.dev/core";
import { jsx, jsxs } from "@qwik.dev/core/jsx-runtime";
import { state } from './sibling';

const useData = () => {
	return useStore({
		count: 0
	});
}

export const App = /*#__PURE__*/ componentQrl(inlinedQrl(()=>{
	const store = useData();
	return /*#__PURE__*/ jsxs("div", {
		children: [
			/*#__PURE__*/ jsxs("p", {
				children: [
					"Count: ",
					store.count
				]
			}),
			/*#__PURE__*/ jsx("p", {
				children: /*#__PURE__*/ jsx("button", {
					onClick$: inlinedQrl(()=>{
						const [store] = useLexicalScope();
						return store.count++;
					}, "App_component_div_p_button_onClick_8dWUa0cJAr4", [
						store
					]),
					children: "Click"
				})
			})
		]
	});
}, "App_component_AkbU84a8zes"));

"#;
	let code = r#"
import { component$, $ } from '@qwik.dev/core';
import { state } from './sibling';

export const Local = component$(() => {
	return (
		<div>{state}</div>
	)
});
"#;
	let res = transform_modules(TransformModulesOptions {
		src_dir: "/path/to/app/src/thing".into(),
		root_dir: Some("/path/to/app/".into()),
		input: vec![
			TransformModuleInput {
				code: dep.into(),
				path: "../../node_modules/dep/dist/lib.mjs".into(),
				dev_path: None,
			},
			TransformModuleInput {
				code: code.into(),
				path: "components/main.tsx".into(),
				dev_path: None,
			},
		],
		source_maps: true,
		minify: MinifyMode::Simplify,
		explicit_extensions: true,
		mode: EmitMode::Test,
		// filler to maintain line offsets
		entry_strategy: EntryStrategy::Segment,
		transpile_ts: true,
		transpile_jsx: true,
		preserve_filenames: false,
		core_module: None,
		scope: None,
		strip_exports: None,
		strip_ctx_name: None,
		strip_event_handlers: false,
		reg_ctx_name: None,
		is_server: None,
	});
	snapshot_res!(&res, "".into());
}
#[test]
fn consistent_hashes() {
	let code = r#"
import { component$, $ } from '@qwik.dev/core';
import mongo from 'mongodb';

export const Greeter = component$(() => {
	// Double count watch
	useTask$(async () => {
		await mongo.users();
	});
	return (
		<div>
			<div onClick$={() => {}}/>
			<div onClick$={() => {}}/>
			<div onClick$={() => {}}/>
		</div>
	)
});

"#;
	let options = vec![
		(EmitMode::Test, EntryStrategy::Segment, true),
		(EmitMode::Test, EntryStrategy::Single, true),
		(EmitMode::Test, EntryStrategy::Component, true),
		// (EmitMode::Test, EntryStrategy::Inline, true),
		(EmitMode::Prod, EntryStrategy::Segment, true),
		(EmitMode::Prod, EntryStrategy::Single, true),
		(EmitMode::Prod, EntryStrategy::Component, true),
		// (EmitMode::Prod, EntryStrategy::Inline, true),
		(EmitMode::Dev, EntryStrategy::Segment, true),
		(EmitMode::Dev, EntryStrategy::Single, true),
		(EmitMode::Dev, EntryStrategy::Component, true),
		// (EmitMode::Dev, EntryStrategy::Inline, true),
		(EmitMode::Test, EntryStrategy::Segment, false),
		(EmitMode::Test, EntryStrategy::Single, false),
		(EmitMode::Test, EntryStrategy::Component, false),
		// (EmitMode::Test, EntryStrategy::Inline, false),
		(EmitMode::Prod, EntryStrategy::Segment, false),
		(EmitMode::Prod, EntryStrategy::Single, false),
		(EmitMode::Prod, EntryStrategy::Component, false),
		// (EmitMode::Prod, EntryStrategy::Inline, false),
		(EmitMode::Dev, EntryStrategy::Segment, false),
		(EmitMode::Dev, EntryStrategy::Single, false),
		(EmitMode::Dev, EntryStrategy::Component, false),
		// (EmitMode::Dev, EntryStrategy::Inline, false),
	];

	let res = transform_modules(TransformModulesOptions {
		src_dir: "./thing".into(),
		input: vec![
			TransformModuleInput {
				code: code.into(),
				path: "main.tsx".into(),
				dev_path: None,
			},
			TransformModuleInput {
				code: code.into(),
				path: "components/main.tsx".into(),
				dev_path: None,
			},
		],
		source_maps: true,
		minify: MinifyMode::Simplify,
		root_dir: None,
		explicit_extensions: true,
		mode: EmitMode::Test,
		// filler to maintain line offsets
		entry_strategy: EntryStrategy::Segment,
		transpile_ts: true,
		transpile_jsx: true,
		preserve_filenames: false,
		scope: None,
		core_module: None,
		reg_ctx_name: None,
		strip_exports: None,
		strip_ctx_name: None,
		strip_event_handlers: false,
		is_server: None,
	});
	let ref_segments: Vec<_> = res
		.unwrap()
		.modules
		.into_iter()
		.flat_map(|module| module.segment)
		.collect();

	for (i, option) in options.into_iter().enumerate() {
		let res = transform_modules(TransformModulesOptions {
			src_dir: "./thing".into(),
			input: vec![
				TransformModuleInput {
					code: code.into(),
					path: "main.tsx".into(),
					dev_path: None,
				},
				TransformModuleInput {
					code: code.into(),
					path: "components/main.tsx".into(),
					dev_path: None,
				},
			],
			root_dir: None,
			source_maps: false,
			minify: MinifyMode::Simplify,
			explicit_extensions: true,
			mode: option.0,
			// filler to maintain line offsets
			entry_strategy: option.1,
			transpile_ts: option.2,
			transpile_jsx: option.2,
			preserve_filenames: false,
			scope: None,
			core_module: None,
			strip_exports: None,
			strip_ctx_name: None,
			strip_event_handlers: false,
			reg_ctx_name: None,
			is_server: None,
		});

		let segments: Vec<_> = res
			.unwrap()
			.modules
			.into_iter()
			.flat_map(|module| module.segment)
			.collect();

		assert_eq!(segments.len(), ref_segments.len());

		for (a, b) in segments.iter().zip(ref_segments.iter()) {
			assert_eq!(
				get_hash(a.name.as_ref()),
				get_hash(b.name.as_ref()),
				"INDEX: {}\n\n{:#?}\n\n{:#?}\n\n{:#?}\n\n{:#?}",
				i,
				a,
				b,
				segments,
				ref_segments
			);
		}
	}
}

#[test]
fn issue_5008() {
	test_input!(TestInput {
		code: r#"
		import { component$, useStore } from "@qwik.dev/core";

		export default component$(() => {
		const store = useStore([{ value: 0 }]);
		return (
			<>
			<button onClick$={() => store[0].value++}>+1</button>
			{store.map(function (v, idx) {
				return <div key={"fn_" + idx}>Function: {v.value}</div>;
			})}
			{store.map((v, idx) => (
				<div key={"arrow_" + idx}>Arrow: {v.value}</div>
			))}
			</>
		);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_of_synchronous_qrl() {
	test_input!(TestInput {
		code: r#"
		import { sync$, component$ } from "@qwik.dev/core";

		export default component$(() => {
		return (
			<>
				<input onClick$={sync$(function(event, target) {
					// comment should be removed
					event.preventDefault();
				})}/>
				<input onClick$={sync$((event, target) => {
					event.preventDefault();
				})}/>
				<input onClick$={sync$((event, target) => event.preventDefault())}/>
			</>
		);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_destructure_args() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from "@qwik.dev/core";

		// the count results in _fnSignal because of the rename
		// would be nice to consider it a prop too
		export default component$(({ message, id, count: c, ...rest }: Record<string, any>) => {
			const renders = useStore({ renders: 0 }, { reactive: false });
			renders.renders++;
			const rerenders = renders.renders + 0;
			return (
				<div id={id}>
					<span {...rest}>
					{message} {c}
					</span>
					<div class="renders">{rerenders}</div>
				</div>
			);
		}
		);
	"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn destructure_args_inline_cmp_block_stmt() {
	test_input!(TestInput {
		code: r#"
		export default ({ data }: { data: any }) => {
          return (
            <div
              data-is-active={data.selectedOutputDetail === 'options'}
              onClick$={() => {
                data.selectedOutputDetail = 'options';
              }}
            />
          );
		};
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn destructure_args_inline_cmp_block_stmt2() {
	test_input!(TestInput {
		code: r#"
		export default (props: { data: any }) => {
		  const { data } = props;
          return (
            <div
              data-is-active={data.selectedOutputDetail === 'options'}
              onClick$={() => {
                data.selectedOutputDetail = 'options';
              }}
            />
          );
		};
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn destructure_args_inline_cmp_expr_stmt() {
	test_input!(TestInput {
		code: r#"
		export default ({ data }: { data: any }) =>
            <div
              data-is-active={data.selectedOutputDetail === 'options'}
              onClick$={() => {
                data.selectedOutputDetail = 'options';
              }}
            />;
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn destructure_args_colon_props() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from "@qwik.dev/core";
		export default component$((props) => {
			const { 'bind:value': bindValue } = props;
			return (
				<>
				{bindValue}
				</>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn destructure_args_colon_props2() {
	test_input!(TestInput {
		code: r#"
		import { component$, useSignal } from "@qwik.dev/core";
		export default component$((props) => {
			const { 'bind:value': bindValue } = props;
			const test = useSignal(bindValue);
			return (
				<>
				{test.value}
				</>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn destructure_args_colon_props3() {
	test_input!(TestInput {
		code: r#"
		import { component$, useSignal } from "@qwik.dev/core";
		export default component$((props) => {
			const { test, ...rest } = props;
			const test = useSignal(rest['bind:value']);
			return (
				<>
				{test.value}
				</>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

// Regression for #8638: when a helper arrow destructures a parameter and
// reassigns one of those bindings, the props-destructuring pass must refuse
// the rewrite. Pre-fix the param was renamed to `_rawProps` and reads were
// rewritten, but assignment LHSs were left dangling — strict-mode
// `ReferenceError` at runtime (and in some shapes the assignment was
// silently dropped by a downstream simplifier).
//
// Each row exercises a different LHS shape: bare AssignExpr, compound /
// nullish, UpdateExpr, object- and array-destructuring assignment, and
// `for-of` / `for-in` heads (including a nested destructuring pattern).
// The signal is direct: the emitted code must not contain `_rawProps`
// (transform refused) and must still contain the original destructured
// name (helper not dropped).
#[test]
fn destructure_args_helper_reassigns_refuses_transform() {
	let cases: &[(&str, &str, &str)] = &[
		(
			"template_literal",
			"ogImage",
			r#"
			export const buildHead = ({ ogImage }: { ogImage?: string }) => {
				if (!ogImage) { ogImage = `fallback-image-url`; }
				return { meta: [{ property: "og:image", content: ogImage }] };
			};
			"#,
		),
		(
			"string_literal",
			"ogImage",
			r#"
			export const buildHead = ({ ogImage }: { ogImage?: string }) => {
				if (!ogImage) { ogImage = "fallback-image-url"; }
				return { meta: [{ property: "og:image", content: ogImage }] };
			};
			"#,
		),
		(
			"nullish_assign",
			"ogImage",
			r#"
			export const buildHead = ({ ogImage }: { ogImage?: string }) => {
				ogImage ??= `fallback-image-url`;
				return { ogImage };
			};
			"#,
		),
		(
			"update_expr",
			"count",
			r#"
			export const bump = ({ count }: { count: number }) => {
				count++;
				return { count };
			};
			"#,
		),
		(
			"object_destructure_assign",
			"ogImage",
			r#"
			export const buildHead = ({ ogImage }: { ogImage?: string }) => {
				({ ogImage } = { ogImage: "fallback-image-url" });
				return { meta: [{ property: "og:image", content: ogImage }] };
			};
			"#,
		),
		(
			"array_destructure_assign",
			"count",
			r#"
			export const bump = ({ count }: { count: number }) => {
				[count] = [42];
				return { count };
			};
			"#,
		),
		(
			"for_of",
			"ogImage",
			r#"
			export const buildHead = ({ ogImage }: { ogImage?: string }) => {
				for (ogImage of ["fallback-image-url"]) {}
				return { meta: [{ property: "og:image", content: ogImage }] };
			};
			"#,
		),
		(
			"for_in",
			"ogImage",
			r#"
			export const buildHead = ({ ogImage }: { ogImage?: string }) => {
				for (ogImage in { "fallback-image-url": 1 }) {}
				return { meta: [{ property: "og:image", content: ogImage }] };
			};
			"#,
		),
		(
			"for_of_destructure",
			"ogImage",
			r#"
			export const buildHead = ({ ogImage }: { ogImage?: string }) => {
				for ({ ogImage } of [{ ogImage: "fallback-image-url" }]) {}
				return { meta: [{ property: "og:image", content: ogImage }] };
			};
			"#,
		),
	];

	for (label, binding, code) in cases {
		let output = test_input!(TestInput {
			code: code.to_string(),
			snapshot: false,
			transpile_ts: true,
			transpile_jsx: true,
			..TestInput::default()
		})
		.unwrap_or_else(|e| panic!("[{}] transform failed: {}", label, e));

		let emitted = &output.modules[0].code;
		assert!(
			!emitted.contains("_rawProps"),
			"[{}] props-destructuring rewrite must be refused on reassignment, but `_rawProps` was injected.\n--- emitted ---\n{}",
			label,
			emitted
		);
		assert!(
			emitted.contains(binding),
			"[{}] expected destructured binding `{}` to be preserved in the emitted code, got:\n{}",
			label,
			binding,
			emitted
		);
	}
}

// Positive control — `for (const it of …)` introduces a fresh inner binding
// that shadows nothing; the refusal guard must NOT over-trigger on for-head
// declarators. Verified by checking that the extracted component body still
// receives the rewrite (`_rawProps.items`).
#[test]
fn destructure_args_inline_cmp_for_of_declarator_no_refusal() {
	let output = test_input!(TestInput {
		code: r#"
		import { component$ } from "@qwik.dev/core";
		export const Cmp = component$(({ items }: { items: string[] }) => {
			const out = [];
			for (const it of items) { out.push(it); }
			return <div>{out.join(",")}</div>;
		});
		"#
		.to_string(),
		snapshot: false,
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	})
	.unwrap();

	let entry = output
		.modules
		.iter()
		.find(|m| m.is_entry)
		.expect("expected an entry-point module for the component body");
	assert!(
		entry.code.contains("_rawProps.items"),
		"expected `items` read to be rewritten to `_rawProps.items` in the extracted body, got:\n{}",
		entry.code
	);
}

#[test]
fn should_handle_dangerously_set_inner_html() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from "@qwik.dev/core";
		const Cmp = component$(() => {
			const htmlSignal = useSignal("<h2><span>I'm a signal value!</span></h2>");
			return (
				<div>
					<div>
						<span id="first" dangerouslySetInnerHTML="vanilla HTML here" />
					</div>
					<div>
						<span id="second" dangerouslySetInnerHTML="<h1>I'm an h1!</h1>" class="after" />
					</div>
					<div>
						<span id="third" dangerouslySetInnerHTML={htmlSignal.value} class="after" />
						<button
							onClick$={() =>
								(htmlSignal.value = "<h2><span>I'm a updated signal value!</span></h2>")
							}
						></button>
					</div>
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_noop_dev_mode() {
	test_input!(TestInput {
		code: r#"
import { component$, useStore, serverStuff$, $ } from '@qwik.dev/core';

export const App = component$(() => {
	const stuff = useStore();
	serverStuff$(async () => {
		// should be removed but keep scope
		console.log(stuff.count)
	})
	serverStuff$(async () => {
		// should be removed
	})

	return (
		<Cmp>
			<p class="stuff"
				shouldRemove$={() => stuff.count}
				onClick$={() => console.log('warn')}
			>
				Hello Qwik
			</p>
		</Cmp>
	);
});
"#
		.to_string(),
		mode: EmitMode::Dev,
		dev_path: Some("/hello/from/dev/test.tsx".into()),
		transpile_ts: true,
		transpile_jsx: true,
		strip_event_handlers: true,
		strip_ctx_name: Some(vec!["server".into()]),
		..TestInput::default()
	});
}

#[test]
fn lib_mode_fn_signal() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';
		export const Counter = component$(() => {
			const count = useSignal(0);

			return (
				<div>
					<p>Count: {count.value}</p>
					<p>
						<button onClick$={() => count.value++}>Increment</button>
					</p>
				</div>
			);
		});
"#
		.to_string(),
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn ternary_prop() {
	test_input!(TestInput {
		code: r#"
		import { component$, $, useSignal } from '@qwik.dev/core';
		export const Cmp = component$(() => {
			const toggleSig = useSignal(false);

			const handleClick$ = $(() => {
				toggleSig.value = !toggleSig.value;
			});

			return (
				<button onClick$={handleClick$} data-open={toggleSig.value ? true : undefined}>
					Removing data-open re-renders
				</button>
			);
		});
		"#
		.to_string(),
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn transform_qrl_in_regular_prop() {
	test_input!(TestInput {
		code: r#"
		import { component$, $ } from '@qwik.dev/core';
		export const Cmp = component$(() =>
			<Cmp foo={$(() => console.log('hi there'))}>Hello Qwik</Cmp>);
		"#
		.to_string(),
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn impure_template_fns() {
	// Should not mark the template function as static
	test_input!(TestInput {
		code: r#"
		import { component$, useSignal } from '@qwik.dev/core';
		const useFoo = (count) => {
			const tag = (s) => {
				const value = typeof s === "string" ? s : s[0];
				return `${value}-${count.value}`;
			}
			return tag;
		}

		export default component$(() => {
			const count = useSignal(0);
			const foo = useFoo(count);
			return (
				<>
					<p>{foo("test")}</p>
					<p>{foo`test`}</p>
					<button onClick$={() => count.value++}>Count up</button>
				</>
			);
		});
		"#
		.to_string(),
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn rename_builder_io() {
	test_input!(TestInput {
		code: r#"
		import { $, component$ } from "@builder.io/qwik";
		import { isDev } from "@builder.io/qwik/build";
		import { stuff } from "@builder.io/qwik-city";
		import { moreStuff } from "@builder.io/qwik-city/more/here";
		import { qwikify$ } from "@builder.io/qwik-react";
		import sdk from "@builder.io/sdk";

		export const Foo = qwikify$(MyReactComponent);

		export const Bar = $("a thing");

		export const App = component$(() => {
			sdk.hello();
			if (isDev) {
				stuff()
			} else {
				moreStuff()
			}
			return "hi";
		});
		"#
		.to_string(),
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn example_component_with_event_listeners_inside_loop() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useStore, useSignal } from '@qwik.dev/core';
export const App = component$(() => {
      const cart = useStore<string[]>([]);
      const results = useSignal(['foo']);
      function loopArrowFn(results: string[]) {
        return results.map((item) => (
          <span
            onClick$={() => {
              cart.push(item);
            }}
          >
            {item}
          </span>
        ));
      }
      function loopForI(results: string[]) {
        const items = [];
        for (let i = 0; i < results.length; i++) {
          items.push(
            <span
              onClick$={() => {
                cart.push(results[i]);
              }}
            >
              {results[i]}
            </span>
          );
        }
        return items;
      }
      function loopForOf(results: string[]) {
        const items = [];
        for (const item of results) {
          items.push(
            <span
              onClick$={() => {
                cart.push(item);
              }}
            >
              {item}
            </span>
          );
        }
        return items;
      }
      function loopForIn(results: string[]) {
        const items = [];
        for (const key in results) {
          items.push(
            <span
              onClick$={() => {
                cart.push(results[key]);
              }}
            >
              {results[key]}
            </span>
          );
        }
        return items;
      }
      function loopWhile(results: string[]) {
        const items = [];
        let i = 0;
        while (i < results.length) {
          items.push(
            <span
              onClick$={() => {
                cart.push(results[i]);
              }}
            >
              {results[i]}
            </span>
          );
          i++;
        }
        return items;
      }
      return (
        <div>
          {results.value.map((item) => (
            <button
              id="second"
              onClick$={() => {
                cart.push(item);
              }}
            >
              {item}
            </button>
          ))}
          {loopArrowFn(results.value)}
          {loopForI(results.value)}
          {loopForOf(results.value)}
          {loopForIn(results.value)}
          {loopWhile(results.value)}
        </div>
      );
    });
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_wrap_inner_inline_component_prop() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useStore, useSignal } from '@qwik.dev/core';
export default component$((props: { id: number }) => {
      const renders = useStore(
        {
          count: 0,
        },
        { reactive: false }
      );
      renders.count++;
      const rerenders = renders.count + 0;
      const Id = (props: any) => <div>Id: {props.id}</div>;
      return (
        <>
          <Id id={props.id} />
          {rerenders}
        </>
      );
    });
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_wrap_prop_from_destructured_array() {
	test_input!(TestInput {
		code: r#"
		import { component$, useStore, useTask$ } from '@qwik.dev/core';
		import { useForm, useForm2 } from './some-file.ts';

		export const Input = component$<{error: string, error2: string, error3: string}>(
			(props) => {
				useTask$(({ track }) => {
					track(() => props.error);
					track(() => props.error2);
					track(() => props.error3);
				});

				return (
					<>
					</>
				);
			}
		);

		export default component$(() => {
			const [store, math] = [useStore({errors: {}}), Math.random()];
			const [[store2]] = [[useStore({errors: {}})]];
			const { store3, math4 } = { store3: useStore({errors: {}}), math4: Math.random() };
			const math2 = [Math.random()];
			const { math3 } = { math3: Math.random() };
			const [store4] = useForm();
			const {store5} = useForm2();

			return (
				<div>
					<button onClick$={() => {
						store.errors.test = store.errors.test ? undefined : 'ERROR TEST';
					}}>click</button>
					<Input 
						error={store.errors.test}
						error2={store2.errors.test}
						error3={store3.errors.test}
						error4={store4.errors.test}
						error5={store5.errors.test}
						math={math}
						math2={math2}
						math3={math3}
						math4={math4}
					/>
				</div>
			);
		});
		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_wrap_object_with_fn_signal() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';
export default component$((props) => {
	// not destructure it so it is a var prop
	const item = props.something.count;
	return (
		<>
			<div data-no-wrap={item ? item * 2 : null} data-wrap={props.myobj.id + "test"}></div>
		</>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_mark_props_as_var_props_for_inner_cmp() {
	test_input!(TestInput {
		code: r#"
import { component$, useResource$, Resource } from "@qwik.dev/core";
import { type ModelProps } from "./modelMenu";
import { serverImg } from "~/routes/(authenticated)/layout";

export const Image = component$((props) => {
  return (
    <>
      <img src={`${props.src}`} />
    </>
  );
});

export const ModelImg = component$<ModelProps>((props) => {
  const imgLoc = useResource$(async ({ track }) => {
    track(() => props.store.model);
    return await serverImg('some.png');
  });
  return (
    <>
      <Resource
        value={imgLoc}
        onRejected={() => <p>error ...</p>}
        onResolved={(res) =>
          res && (
            <>
              <Image
                src={res}
              />
            </>
          )
        }
      />
    </>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_wrap_fn() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from "@qwik.dev/core";
import { A } from "./componentA";

export const Cmp = component$(() => {
	const currentStep = useSignal('STEP_1');
	const currentType = useSignal<'NEXT' | 'PREVIOUS'>('PREVIOUS');

	const getStep = (step: string, type: 'NEXT' | 'PREVIOUS') => {
		return step === 'STEP_1' ? 'STEP_2' : 'STEP_1';
	};

	return (
		<>
			<button onClick$={() => (currentType.value = 'NEXT')}>CLICK</button>
			<A href={getStep(currentStep.value, currentType.value)} />
		</>
	);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn issue_7216_add_test() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@builder.io/qwik';
export default component$((props) => {
  return (<p 
		onHi$={() => 'hi'} 
		{...props.foo} 
		onHello$={props.helloHandler$} 
		{...props.rest} 
		onVar$={props.onVarHandler$} 
		onConst$={() => 'const'} 
		asd={"1"}
	/>);
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_wrap_store_expression() {
	test_input!(TestInput {
		code: r#"
		import { component$, useStore } from '@qwik.dev/core';

		export default component$(() => {
			const panelStore = useStore(() => ({
				active: 'Input',
				list: PANELS,
			}));

			return (
				<div
					stuff={panelStore.active ? 'yes' : 'no'}
					class={{
						'too-long-to-wrap': true,
						'examples-panel-input': panelStore.active === 'Input',
						'examples-panel-output': panelStore.active === 'Output',
						'examples-panel-console': panelStore.active === 'Console',
					}}
				/>
			);
		});
		export const PANELS: ActivePanel[] = ['Examples', 'Input', 'Output', 'Console'];
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_wrap_var_template_string() {
	test_input!(TestInput {
		code: r#"
		import { component$, useComputed$ } from '@qwik.dev/core';
		import { inlineTranslate } from 'translate-lib';

		export default component$(() => {
			const t = inlineTranslate();

			const productTitle = useComputed$(() => {
				return 'Test title';
			});

			return (
				<img 
					attr={t('home.imageAlt.founded-product:')}
					alt={`${t('home.imageAlt.founded-product:')} ${productTitle.value}`} />
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_wrap_type_asserted_variables_in_template() {
	test_input!(TestInput {
		code: r#"
		import { component$, useSignal } from '@qwik.dev/core';

		export default component$(() => {
			const count = useSignal(0);
			return (
				<div>
					{(count as any).value}
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_wrap_logical_expression_in_template() {
	test_input!(TestInput {
		code: r#"
		import { component$, useSignal } from '@qwik.dev/core';

		export default component$(() => {
			const count = useSignal(0);
			const count2 = useSignal(0);
			return (
				<div>
					{(count || count2).value}
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_wrap_ternary_function_operator_with_fn() {
	test_input!(TestInput {
		code: r#"
		import { component$, useSignal } from '@qwik.dev/core';


		export default component$(() => {
		const toggle = useSignal(true);
		const t = (key: string) => key;
		return (
			<button
				type="button"
				title={
				toggle.value !== ''
					? t('app.message.exists@@there is a message for you')
					: t('app.message.not_exists@@click to get a message!')
				}
			></button>
		);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_split_spread_props() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		export default component$((props) => {
			return (
				<div {...props}></div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_split_spread_props_with_additional_prop() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		export default component$((props) => {
			return (
				<div {...props} test="test"></div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_split_spread_props_with_additional_prop2() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		export default component$((props) => {
			return (
				<div test="test" {...props}></div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_split_spread_props_with_additional_prop3() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';
		import { Foo } from './foo';

		export default component$((props) => {
			return (
				<Foo s={Math.random()} {...props} hello {...globalThis.nothing} />
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_split_spread_props_with_additional_prop4() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		export default component$((props: any) => {
			return <button {...props} onClick$={() => props.onClick$()}></button>;
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_split_spread_props_with_additional_prop5() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		function Hola(props: any) {
			return <div {...props}></div>;
		}

		export default component$(() => {
		return <Hola>
			<div>1</div>
			<div>2</div>
		</Hola>;
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_generate_conflicting_props_identifiers() {
	test_input!(TestInput {
		code: r#"
		import { component$, useComputed$, useTask$ } from '@qwik.dev/core'

		export default component$(({ color, ...props }) => {
		useComputed$(() => color)

		useTask$(() => {
			props.checked
		})

		return 'hi'
		})
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		// important to use hoist entry strategy to test this case
		// only in hoist mode there was an issue with conflicting props identifiers
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
}

#[test]
fn should_convert_rest_props() {
	test_input!(TestInput {
		code: r#"
		import { component$, useTask$ } from '@qwik.dev/core'

		export default component$<any>(({ ...props }) => {
		useTask$(() => {
			props.checked
		})

		return 'hi'
		})
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_merge_attributes_with_spread_props() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		export default component$((props) => {
			return <div {...props} class={[props.class, 'component']} />;
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_merge_attributes_with_spread_props_before_and_after() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		export default component$((props) => {
			return <div {...props} class={[props.class, 'component']} {...props} />;
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_move_over_side_effects() {
	test_input!(TestInput {
		code: r#"
		export const $promoteToRoot$ = (ref: SeenRef) => {
			const path = $getObjectPath$(ref) as string;
			// should stay before the push
			const idx = roots.length;
			roots.push(new BackRef(path));
			ref.$parent$ = null;
			ref.$index$ = idx;
			return idx;
		};
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_ignore_null_inlined_qrl() {
	test_input!(TestInput {
		code: r#"
		import { inlinedQrl } from '@qwik.dev/core';

		const foo = inlinedQrl(null, 'some_hash');
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn hoisted_fn_signal_in_loop() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const App = component$(() => {
  const data = { value: [
    { value: { id: 1, selected: { value: true } } },
    { value: { id: 2, selected: { value: false } } },
    { value: { id: 3, selected: { value: true } } }
  ]};
  
  return (
    <table>
      {data.value.map((row) => {
        return (
          <tr
            key={row.value.id}
            class={row.value.selected.value ? "danger" : ""}
          >
            <td>{row.value.id}</td>
          </tr>
        );
      })}
    </table>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_convert_jsx_events() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		const ManyEventsComponent = component$(() => {
			return (
				<div>
					<button
						onClick$={() => {}}
						onDblClick$={() => {}}
					>
						click
					</button>
					<button
						onClick$={() => {}}
						onBlur$={() => {}}
						on-anotherCustom$={() => {}}
						document:onFocus$={() => {}}
						window:onClick$={() => {}}
					>
						click
					</button>
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_convert_passive_jsx_events() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		const PassiveEventsComponent = component$(() => {
			return (
				<div>
					<button passive:click onClick$={() => {}}>
						click
					</button>
					<button passive:scroll passive:touchstart window:onScroll$={() => {}} document:onTouchStart$={() => {}}>
						scroll
					</button>
					<button passive:mouseover>
						noop
					</button>
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_ignore_passive_jsx_events_without_handlers() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		const PassiveOnlyComponent = component$(() => {
			return (
				<div>
					<button passive:click>
						click
					</button>
					<button passive:scroll passive:touchstart>
						scroll
					</button>
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_ignore_preventdefault_with_passive() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		const PassiveOnlyComponent = component$(() => {
			return (
				<div>
					<button passive:click preventdefault:click onClick$={() => {}}>
						click
					</button>
					<button passive:scroll preventdefault:click preventdefault:scroll onClick$={() => {}} onScroll$={() => {}}>
						click
					</button>
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_disable_passive_warning_with_qwik_disable_next_line() {
	test_input!(TestInput {
		code: r#"
		import { component$, sync$ } from '@qwik.dev/core';

		const PassiveOnlyComponent = component$(() => {
			return (
				<div>
					<button onClick$={sync$((event) => {
						// keep me
						event.preventDefault();
					})}>
						click
					</button>
					{/* @qwik-disable-next-line preventdefault-passive-check */}
					<button passive:click preventdefault:click onClick$={() => {}}>
						click
					</button>
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_only_disable_the_next_line() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from '@qwik.dev/core';

		const PassiveOnlyComponent = component$(() => {
			return (
				<div>
					{/* @qwik-disable-next-line preventdefault-passive-check */}
					<button passive:click preventdefault:click onClick$={() => {}}>
						click
					</button>
					<button passive:click preventdefault:click onClick$={() => {}}>
						click
					</button>
				</div>
			);
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_disable_multiple_rules_from_single_directive() {
	test_input!(TestInput {
		code: r#"
		import { component$, useTask$ } from '@qwik.dev/core';

		export const useMemo$ = (qrl) => {
			useTask$(qrl);
		};

		export const App = component$(() => {
			/* @qwik-disable-next-line C05, preventdefault-passive-check */
			useMemo$(() => <button passive:click preventdefault:click onClick$={() => {}}>click</button>);
			return <div />;
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_event_names_without_jsx_transpile() {
	test_input!(TestInput {
		code: r#"
import { component$, $ } from '@qwik.dev/core';
import mongo from 'mongodb';

export const Greeter = component$(() => {
	// Double count watch
	useTask$(async () => {
		await mongo.users();
	});
	return (
		<div>
			<div onClick$={() => {}}/>
			<div onClick$={() => {}}/>
			<div onClick$={() => {}}/>
		</div>
	)
});

"#
		.to_string(),
		transpile_ts: false,
		transpile_jsx: false,
		..TestInput::default()
	});
}

#[test]
fn should_transform_passive_event_names_without_jsx_transpile() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const Greeter = component$(() => {
	return (
		<div>
			<div passive:click onClick$={() => {}}/>
			<div passive:scroll window:onScroll$={() => {}}/>
			<div passive:touchstart document:onTouchStart$={() => {}}/>
			<div passive:mouseover/>
		</div>
	)
});

"#
		.to_string(),
		transpile_ts: false,
		transpile_jsx: false,
		..TestInput::default()
	});
}

#[test]
fn should_not_transform_events_on_non_elements() {
	test_input!(TestInput {
		code: r#"
import { component$, $ } from '@qwik.dev/core';
import { CustomComponent } from './custom-component';
import { AnotherComponent } from './another-component';

export const Greeter = component$(() => {
	return (
		<div>
			<CustomComponent onClick$={() => {}}/>
			{array.map(item => (
				<AnotherComponent onClick$={() => {}}/>
			))}
		</div>
	)
});

"#
		.to_string(),
		transpile_ts: false,
		transpile_jsx: false,
		..TestInput::default()
	});
}

#[test]
fn should_merge_bind_value_and_on_input() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from "@qwik.dev/core";

export const FieldInput = component$(() => {
  const localValue = useSignal("");

  return (
    <input
      onInput$={() => {
        console.log("test");
      }}
      bind:value={localValue}
    />
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_merge_bind_checked_and_on_input() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from "@qwik.dev/core";

export const FieldInput = component$(() => {
  const localValue = useSignal(false);

  return (
    <input
      onInput$={() => {
        console.log("test");
      }}
      bind:checked={localValue}
    />
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_extract_single_qrl() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal, Signal } from '@qwik.dev/core';
export const App = component$(() => {
	const data = useSignal<Signal<any>[]>([]);
	const selectedItem = useSignal<any | null>(null);
    return (
        <div>
          {data.value.map((row) => (
              <tr
                key={untrack(() => row.value.id)}
                class={row.value.selected.value ? "danger" : ""}
              >
                <td class="col-md-1">{row.value.id}</td>
                <td class="col-md-4">
                  <a
                    onClick$={() => {
                      if (selectedItem.value) {
                        selectedItem.value.selected.value = false;
                      }
                      selectedItem.value = row.value;
                      row.value.selected.value = true;
                    }}
                  >
                    {row.value.label.value}
                  </a>
                </td>
                <td class="col-md-1">
                  <a
                    onClick$={() => {
                      const dataValue = untrack(() => data.value);
                      data.value = dataValue.toSpliced(
                        dataValue.findIndex((d) => d.value.id === row.value.id),
                        1,
                      );
                    }}
                  >
                    <span aria-hidden="true">x</span>
                  </a>
                </td>
                <td class="col-md-6" />
              </tr>
          ))}
        </div>
      );
    });
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_extract_single_qrl_2() {
	test_input!(TestInput {
		code: r#"
	  import { component$, useStore, useSignal } from '@qwik.dev/core';
      const Parent = component$(() => {
      const cart = useStore<Cart>([]);
      const results = useSignal(['foo', 'bar']);

      return (
        <div>
          <button id="first" onClick$={() => (results.value = ['item1', 'item2'])}></button>

          {results.value.map((item, key) => (
            <button
              id={'second-' + key}
              onClick$={() => {
                cart.push(item);
              }}
            >
              {item}
            </button>
          ))}
          <ul>
            {cart.map((item) => (
              <li>
                <span>{item}</span>
              </li>
            ))}
          </ul>
        </div>
      );
    });
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_extract_single_qrl_with_index() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal, Signal } from '@qwik.dev/core';
export const App = component$(() => {
	const data = useSignal<Signal<any>[]>([]);
	const selectedItem = useSignal<any | null>(null);
	const clickedIndex = useSignal<number | null>(null);
    return (
        <div>
          {data.value.map((row, idx) => (
              <tr
                key={untrack(() => row.value.id)}
                class={row.value.selected.value ? "danger" : ""}
              >
                <td class="col-md-1">{row.value.id}</td>
                <td class="col-md-4">
                  <a
                    onClick$={() => {
                      if (selectedItem.value) {
                        selectedItem.value.selected.value = false;
                      }
                      selectedItem.value = row.value;
                      row.value.selected.value = true;
					  clickedIndex.value = idx;
                    }}
                  >
                    {row.value.label.value}
                  </a>
                </td>
                <td class="col-md-1">
                  <a
                    onClick$={() => {
                      const dataValue = untrack(() => data.value);
                      data.value = dataValue.toSpliced(
                        dataValue.findIndex((d) => d.value.id === row.value.id),
                        1,
                      );
                    }}
                  >
                    <span aria-hidden="true">x</span>
                  </a>
                </td>
                <td class="col-md-6" />
              </tr>
          ))}
        </div>
      );
    });
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_extract_multiple_qrls_with_item_and_index() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export default component$(() => {
  const loop: string[] = ['abcd', 'xyz'];
  return (
    <div>
      {loop.map((item, index) => {
        return <div onClick$={() => console.log(item)} onHover$={() => console.log(index)}>{item}</div>
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_extract_multiple_qrls_with_item_and_index_and_capture_ref() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const foo = useSignal('hi');
  const bar = useSignal('ho');
  const loop: string[] = ['abcd', 'xyz'];
  return (
    <div>
      {loop.map((item, index) => {
        return <div onClick$={() => console.log(item, foo.value)} onHover$={() => console.log(bar.value, index)}>{item}</div>
      })}
    </div>
  );
});

"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_extract_single_qrl_with_nested_components() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal, Signal } from '@qwik.dev/core';
const Foo = component$(() => {
  const data = useSignal<Signal<any>[]>([]);
  const Inner = component$((props) => {
    const data = props.data
    return <div>{data.value.map(item => <p onClick$={() => console.log(item.value.id)}>{item.value.id}</p>)}</div>
  })
  return <Inner data={data} />
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_component_with_normal_function() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal, Signal } from '@qwik.dev/core';
const Foo = component$(function() {
  const data = useSignal<Signal<any>[]>([]);
  const Inner = component$(function(props) {
    const data = props.data
    return <div>{data.value.map(item => <p onClick$={() => console.log(item.value.id)}>{item.value.id}</p>)}</div>
  })
  return <Inner data={data} />
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_nested_loops() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal, Signal } from '@qwik.dev/core';
const Foo = component$(function() {
  const data = useSignal<Signal<any>[]>([]);
  const data2 = useSignal<Signal<any>[]>([]);
  return <div>
	{data.value.map(row => (
	  <div onClick$={() => console.log(row.value.id)}>
		{data2.value.map(item => (
		  <p onClick$={() => console.log(row.value.id, item.value.id)}>{row.value.id}-{item.value.id}</p>
		))}
	  </div>
	))}
  </div>;
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_multiple_event_handlers() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal, Signal } from '@qwik.dev/core';
const Foo = component$(function() {
  const data = useSignal<Signal<any>[]>([]);
  return <div>
	{data.value.map(row => (
	  <div onClick$={() => console.log(row.value.id)} onMouseOver$={() => console.log('over' + row.value.id)}>
		<p onClick$={() => console.log('inner' + row.value.id)}>{item.value.id}</p>
	  </div>
	))}
  </div>;
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_multiple_event_handlers_case2() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal, Signal } from '@qwik.dev/core';
const Foo = component$(function() {
  const data = useSignal<Signal<any>[]>([]);
  return <div>
	{data.value.map((row, idx) => (
	  <div onClick$={() => console.log(row.value.id, idx)} onMouseOver$={() => console.log('over' + row.value.id)}>
		<p onClick$={() => console.log('inner' + row.value.id)}>{item.value.id}</p>
	  </div>
	))}
  </div>;
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_merge_on_input_and_bind_value() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from "@qwik.dev/core";

export const FieldInput = component$(() => {
  const localValue = useSignal("");

  return (
    <input
      bind:value={localValue}
      onInput$={() => {
        console.log("test");
      }}
    />
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_merge_on_input_and_bind_checked() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from "@qwik.dev/core";

export const FieldInput = component$(() => {
  const localValue = useSignal(false);

  return (
    <input
      bind:checked={localValue}
      onInput$={() => {
        console.log("test");
      }}
    />
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_qrls_in_ternary_expression() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal } from "@qwik.dev/core";

export const FieldInput = component$(() => {
	const enabled = useSignal(false);
	const input = useSignal("");

  return (
     <input
		id="input"
		onInput$={
		enabled.value
			? $((ev, el) => {
				input.value = el.value;
			})
			: undefined
		}
		onFocus$={() => {
			enabled.value = true;
		}}
	/>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_transform_bind_value_in_var_props_for_jsx_split() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal } from "@qwik.dev/core";

export const FieldInput = component$((props) => {
	const input = useSignal("");

  return (
		<>
			{/* var props */}
			<input
				bind:value={input}
				{...props}
			/>
			{/* const props */}
			<input
				{...props}
				bind:value={input}
			/>
		</>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_transform_bind_checked_in_var_props_for_jsx_split() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal } from "@qwik.dev/core";

export const FieldInput = component$((props) => {
	const input = useSignal(true);

  return (
		<>
			{/* var props */}
			<input
				bind:checked={input}
				{...props}
			/>
			{/* const props */}
			<input
				{...props}
				bind:checked={input}
			/>
		</>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_move_bind_value_to_var_props() {
	test_input!(TestInput {
		code: r#"
import { $, component$, useSignal } from "@qwik.dev/core";
import { destructureBindings } from "./destructure-bindings";

export const FieldInput = component$(
  (props) => {
    const initialValues = { value: undefined as string | undefined };
    const rest = destructureBindings(props, initialValues);

    return (
      <input
        {...rest}
        bind:value={finalValue}
		onClick$={() => {
			console.log('clicked');
		}}
      />
    );
  }
);
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_move_props_related_to_iteration_variables_to_var_props() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from "@qwik.dev/core";
import { TestComponent } from "./testComponent";

export const Child = component$(() => {
  const propCounterWithNested = useSignal(() => {
  return {
      data: [
        { attributeInArray: { counter: globalThis.propsCounter++ } },
        { attributeInArray: { counter: globalThis.propsCounter } },
      ],
    };
  })
  return (
    <div>
        {propCounterWithNested.value.data.map((item, index) => {
          return (
            <TestComponent
              counter={item.attributeInArray.counter}
              logString="Nested read through array"
              key={index}
            />
          );
        })}
    </div>
  );
})
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_make_component_jsx_split_with_bind() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const Cmp = Math.random() >= 0.5 ? 'button' : 'input'
  const sig = useSignal(0)
  return (
    <div>
      <Cmp bind:value={sig} />
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_block_scoped_variables_in_loop() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const arr = useSignal(['a', 'b'])
  return (
    <div>
      {arr.value.map((val, i) => {
        const index = i+1;
        return <div onClick$={() => console.log(index)}>{val}</div>
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_multiple_block_scoped_variables_in_loop() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const arr = useSignal(['a', 'b'])
  return (
    <div>
      {arr.value.map((val, i) => {
        const index = i+1;
		const value = val.toUpperCase();
        return <div onClick$={() => console.log(value, index)}>{val}</div>
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_block_scoped_variables_and_item_index_in_loop() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const arr = useSignal(['a', 'b'])
  return (
    <div>
      {arr.value.map((val, i) => {
        const index = i+1;
        return <div onClick$={() => console.log(val, i, index)}>{val}</div>
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_multiple_block_scoped_variables_and_item_index_in_loop() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const arr = useSignal(['a', 'b'])
  return (
    <div>
      {arr.value.map((val, i) => {
        const index = i+1;
		const value = val.toUpperCase();
        return <div onClick$={() => console.log(value, index, val, i)}>{val}</div>
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_two_handlers_capturing_different_block_scope_in_loop() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const arr = useSignal(['a', 'b'])
  return (
    <div>
      {arr.value.map((val, i) => {
        const index = i + 1;
        const label = val.toUpperCase();
        return (
          <div>
            <button onClick$={() => console.log(index)}>{val}</button>
            <span onKeyDown$={() => console.log(label)}>{label}</span>
          </div>
        )
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_handlers_capturing_cross_scope_in_nested_loops() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const matrix = useSignal([['a', 'b'], ['c', 'd']]);
  return (
    <div>
      {matrix.value.map((row, i) => {
        const rowIndex = i + 1;
        return (
          <div key={i}>
            {row.map((cell, j) => {
              const cellIndex = j + 1;
              const cellKey = rowIndex + '-' + cellIndex;
              return (
                <span key={j}>
                  <button onClick$={() => console.log(rowIndex, cellIndex)}>{cell}</button>
                  <span onKeyDown$={() => console.log(cellKey, i, j)}>{cellKey}</span>
                </span>
              );
            })}
          </div>
        );
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_same_element_one_handler_with_captures_one_without() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const arr = useSignal(['a', 'b']);
  return (
    <div>
      {arr.value.map((val, i) => {
        const index = i + 1;
        return (
          <div
            onClick$={() => console.log(index)}
            onKeyDown$={() => console.log('no capture')}
          >
            {val}
          </div>
        );
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_nested_loops_handler_captures_only_inner_scope() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const matrix = useSignal([['a', 'b'], ['c', 'd']]);
  return (
    <div>
      {matrix.value.map((row, i) => (
        <div key={i}>
          {row.map((cell, j) => {
            const cellKey = cell + '-' + j;
            return (
              <span key={j}>
                <button onClick$={() => console.log(cellKey)}>{cell}</button>
              </span>
            );
          })}
        </div>
      ))}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_three_nested_loops_handler_captures_outer_only() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const data = useSignal([
    [['a', 'b'], ['c', 'd']],
    [['e', 'f'], ['g', 'h']],
  ]);
  return (
    <div>
      {data.value.map((plane, pi) => {
        const planeId = 'p' + pi;
        return (
          <div key={pi}>
            {plane.map((row, ri) => (
              <div key={ri}>
                {row.map((cell, ci) => (
                  <button key={ci} onClick$={() => console.log(planeId)}>
                    {cell}
                  </button>
                ))}
              </div>
            ))}
          </div>
        );
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_handler_in_for_of_loop() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const arr = useSignal(['a', 'b']);
  const items = [];
  for (const val of arr.value) {
    items.push(
      <div onClick$={() => console.log(val)}>{val}</div>
    );
  }
  return <div>{items}</div>;
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_transform_loop_multiple_handler_with_different_captures() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const arr = useSignal(['a', 'b']);
  return (
    <div>
      {arr.value.map((val, i) => {
        const plusOne = i + 1;
        const plusTwo = i + 2;
        const double = i * 2;
        return (
          <div
            onClick$={() => console.log(plusOne, double)}
            onKeyDown$={() => console.log(plusTwo, double)}
          >
            {val}
          </div>
        );
      })}
    </div>
  );
});
"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

fn get_hash(name: &str) -> String {
	name.split('_').last().unwrap().into()
}

fn get_segment_hash_by_ctx_name(output: &TransformOutput, ctx_name: &str) -> String {
	let hashes: Vec<_> = output
		.modules
		.iter()
		.filter_map(|module| module.segment.as_ref())
		.filter(|segment| segment.ctx_name.as_ref() == ctx_name)
		.map(|segment| get_hash(segment.name.as_ref()))
		.collect();
	assert_eq!(
		hashes.len(),
		1,
		"Expected exactly one segment for {}, got {:?}",
		ctx_name,
		hashes
	);
	hashes[0].clone()
}

fn get_segment_name_by_ctx_name(output: &TransformOutput, ctx_name: &str) -> String {
	let names: Vec<_> = output
		.modules
		.iter()
		.filter_map(|module| module.segment.as_ref())
		.filter(|segment| segment.ctx_name.as_ref() == ctx_name)
		.map(|segment| segment.name.to_string())
		.collect();
	assert_eq!(
		names.len(),
		1,
		"Expected exactly one segment for {}, got {:?}",
		ctx_name,
		names
	);
	names[0].clone()
}

struct TestInput {
	pub code: String,
	pub filename: String,
	pub dev_path: Option<String>,
	pub src_dir: String,
	pub root_dir: Option<String>,
	pub entry_strategy: EntryStrategy,
	pub minify: MinifyMode,
	pub transpile_ts: bool,
	pub transpile_jsx: bool,
	pub preserve_filenames: bool,
	pub explicit_extensions: bool,
	pub snapshot: bool,
	pub mode: EmitMode,
	pub core_module: Option<String>,
	pub scope: Option<String>,
	pub strip_exports: Option<Vec<String>>,
	pub reg_ctx_name: Option<Vec<String>>,
	pub strip_ctx_name: Option<Vec<String>>,
	pub strip_event_handlers: bool,
	pub is_server: Option<bool>,
}

#[test]
fn example_segment_variable_migration() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

// This helper is only used by App component, so it should be migrated to its segment
const helperFn = (msg) => {
	console.log('Helper: ' + msg);
	return msg.toUpperCase();
};

// This shared variable is used by multiple segments, so it should stay at root
const SHARED_CONFIG = { value: 42 };

// This is an export, so it must stay at root
export const publicHelper = () => console.log('public');

export const App = component$(() => {
	const result = helperFn('hello');
	return <div>{result} {SHARED_CONFIG.value}</div>;
});

export const Other = component$(() => {
	return <div>{SHARED_CONFIG.value}</div>;
});
"#
		.to_string(),
		snapshot: true,
		..TestInput::default()
	});
}

/// Regression test: when a root variable is pulled in as a transitive dependency
/// for migration to segment A, but is also directly used by segment B, it must NOT
/// be migrated. Otherwise segment B loses access to it (the export is removed).
/// This reproduces the qwik-router bug where `currentScrollState` and `saveScrollHistory`
/// were migrated to the useTask segment but also needed by the goto segment.
#[test]
fn variable_migration_transitive_dep_used_by_other_segment() {
	let res = test_input!(TestInput {
		code: r#"
import { component$, $ } from '@qwik.dev/core';

// scrollState is used by both segments - must NOT be migrated
const scrollState = (el) => ({ x: el.scrollLeft, y: el.scrollTop });

// saveScroll is used by both segments - must NOT be migrated
const saveScroll = (s) => history.replaceState(s, '');

// bigHelper depends on scrollState and saveScroll, used only by App
const bigHelper = (el) => {
  const s = scrollState(el);
  saveScroll(s);
  return s;
};

export const App = component$(() => {
  // Uses bigHelper (which transitively uses scrollState and saveScroll)
  const s = bigHelper(document.body);
  return <div>{s.x}</div>;
});

export const Other = component$(() => {
  // Directly uses scrollState and saveScroll
  const s = scrollState(document.body);
  saveScroll(s);
  return <div>{s.y}</div>;
});
"#
		.to_string(),
		snapshot: true,
		..TestInput::default()
	});

	// Verify the fix: scrollState and saveScroll must be importable by both segments
	let output = res.unwrap();
	let other_segment = output
		.modules
		.iter()
		.find(|m| m.path.contains("Other_component"))
		.expect("Other_component segment should exist");

	// scrollState must be imported (not missing) in the Other segment
	assert!(
		other_segment.code.contains("scrollState")
			&& (other_segment.code.contains("import")
				|| other_segment.code.contains("const scrollState")),
		"scrollState must be available in Other segment (imported or inlined), got:\n{}",
		other_segment.code
	);
	assert!(
		other_segment.code.contains("saveScroll")
			&& (other_segment.code.contains("import")
				|| other_segment.code.contains("const saveScroll")),
		"saveScroll must be available in Other segment (imported or inlined), got:\n{}",
		other_segment.code
	);
}

#[test]
fn root_level_self_referential_qrl() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

// Root-level self-referential component
// The QRL should NOT be hoisted before the component function is defined
// or should use two-phase emission to avoid forward reference
export const Tree = component$((props) => {
	return (
		<div>
			{props.label}
			{props.children && props.children.map((child) => <Tree {...child} />)}
		</div>
	);
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn root_level_self_referential_qrl_inline() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

// Root-level self-referential component
// The QRL should NOT be hoisted before the component function is defined
// or should use two-phase emission to avoid forward reference
export const Tree = component$((props) => {
	return (
		<div>
			{props.label}
			{props.children && props.children.map((child) => <Tree {...child} />)}
		</div>
	);
});
"#
		.to_string(),
		filename: "./node_modules/qwik-tree/index.qwik.jsx".to_string(),
		transpile_jsx: true,
		is_server: Some(true),
		entry_strategy: EntryStrategy::Inline,
		mode: EmitMode::Dev,
		..TestInput::default()
	});
}

#[test]
fn component_level_self_referential_qrl() {
	test_input!(TestInput {
		code: r#"
import { component$, useAsync$ } from '@qwik.dev/core';
		
// Component-level self-referential component
export const Foo = component$((props) => {
	const sig = useAsync$(async ({cleanup}) => {
		const timer = setInterval(() => {
			sig.value++;
		}, 1000);
		cleanup(() => clearInterval(timer));
		return 0;
	});
	const other = useAsync$(async ({cleanup}) => {
		const timer = setInterval(() => {
			other.value++;
		}, 900);
		cleanup(() => clearInterval(timer));
		return 0;
	});
	return (
		<div>
			{other.value}
		</div>
	);
});
"#
		.to_string(),
		..TestInput::default()
	});
}

#[test]
fn example_self_referential_component_migration() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

// Self-referential component: the Nested component references itself in its JSX
// This should be migrated to its segment using two-phase emission (let + assign)
// to avoid Temporal Dead Zone errors
export const Nested = component$(() => {
	return (
		<div>
			<Nested />
		</div>
	);
});

// Another self-referential component with conditional rendering
export const RecursiveList = component$((props) => {
	if (props.depth === 0) return <div>End</div>;
	return (
		<div>
			Level {props.depth}
			<RecursiveList depth={props.depth - 1} />
		</div>
	);
});

// Mutually recursive components: A references B, B references A
const ComponentA = component$(() => {
	return (
		<div>
			A
			<ComponentB />
		</div>
	);
});

const ComponentB = component$(() => {
	return (
		<div>
			B
			<ComponentA />
		</div>
	);
});

export const MutualExample = component$(() => {
	return <ComponentA />;
});
"#
		.to_string(),
		snapshot: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

// Non-snapshot tests for robust import resolution
#[test]
fn import_collision_with_renaming() {
	// Test that imports are correctly resolved from global context without fallback
	// This verifies the fix for SyntaxContext mismatch issues
	let res = test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const Test = component$(() => {
	return <div>Test</div>;
});
"#
		.to_string(),
		snapshot: false,
		..TestInput::default()
	});

	assert!(
		res.is_ok(),
		"Transform should succeed without fallback errors"
	);
	let output = res.unwrap();

	// Verify the entry module was created
	let entry = output.modules.iter().find(|m| m.is_entry);
	assert!(entry.is_some(), "Should have entry module");

	// Verify output modules contain generated code
	assert!(
		!output.modules.is_empty(),
		"Should generate at least one module"
	);
}

#[test]
fn explicit_imports_for_dev_qrls() {
	// Test that dev mode transform succeeds with explicit imports for hoisted items
	let res = test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const Test = component$(() => {
	const items = [1, 2, 3];
	return (
		<div>
			{items.map(item => <div>{item}</div>)}
		</div>
	);
});
"#
		.to_string(),
		mode: EmitMode::Dev,
		snapshot: false,
		..TestInput::default()
	});

	assert!(res.is_ok(), "Transform should succeed in dev mode");
	let output = res.unwrap();

	// Verify all modules have valid import structure
	for module in &output.modules {
		// Each module's code should be valid (contain component or segment code)
		assert!(
			!module.code.is_empty(),
			"Module {} should have code",
			module.path
		);
	}
}

#[test]
fn import_backed_qrl_hash_matches_across_relative_import_paths() {
	let code = r#"
import { $ } from '@qwik.dev/core';
import { name } from './utils/value';

export const value = $(name);
"#;

	let first = test_input!(TestInput {
		code: code.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();
	let second = test_input!(TestInput {
		code: code.replace("./utils/value", "../shared/../utils/value"),
		filename: "src/routes/nested/b.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();

	assert_eq!(
		get_segment_hash_by_ctx_name(&first, "$"),
		get_segment_hash_by_ctx_name(&second, "$")
	);
}

#[test]
fn import_backed_qrl_hash_matches_namespace_member() {
	let named = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import { name } from './utils/value';

export const value = $(name);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();

	let namespace = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import * as ns from './utils/value';

export const value = $(ns.name);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();

	assert_eq!(
		get_segment_hash_by_ctx_name(&named, "$"),
		get_segment_hash_by_ctx_name(&namespace, "$")
	);
}

#[test]
fn import_backed_qrl_hash_normalizes_backslashes_and_non_relative_paths() {
	let relative = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import { name } from '.\\utils\\value';

export const value = $(name);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();
	let relative_normalized = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import { name } from './utils/value';

export const value = $(name);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();
	let package_import = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import { name } from '@pkg/utils/value';

export const value = $(name);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();

	assert_eq!(
		get_segment_hash_by_ctx_name(&relative, "$"),
		get_segment_hash_by_ctx_name(&relative_normalized, "$")
	);
	assert_ne!(
		get_segment_hash_by_ctx_name(&relative_normalized, "$"),
		get_segment_hash_by_ctx_name(&package_import, "$")
	);
}

#[test]
fn import_backed_qrl_default_import_uses_import_based_prefix() {
	let output = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import css from './style.css';

export const value = $(css);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();

	let name = get_segment_name_by_ctx_name(&output, "$");
	assert!(
		name.starts_with("style_css_"),
		"Expected import-based prefix, got {}",
		name
	);
}

#[test]
fn import_backed_qrl_hash_falls_back_for_unsupported_paths() {
	let base = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import * as ns from './utils/value';

export const value = $(ns.name);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();
	let nested_member = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import * as ns from './utils/value';

export const value = $(ns.name.deep);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();
	let computed_member = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import * as ns from './utils/value';

const key = 'name';
export const value = $(ns[key]);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();
	let too_many_dotdots = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import { name } from '../../../value';

export const value = $(name);
"#
		.to_string(),
		filename: "src/routes/a.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();
	let too_many_dotdots_other_file = test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import { name } from '../../../value';

export const value = $(name);
"#
		.to_string(),
		filename: "src/routes/b.tsx".into(),
		snapshot: false,
		..TestInput::default()
	})
	.unwrap();

	assert_ne!(
		get_segment_hash_by_ctx_name(&base, "$"),
		get_segment_hash_by_ctx_name(&nested_member, "$")
	);
	assert_ne!(
		get_segment_hash_by_ctx_name(&base, "$"),
		get_segment_hash_by_ctx_name(&computed_member, "$")
	);
	assert_ne!(
		get_segment_hash_by_ctx_name(&too_many_dotdots, "$"),
		get_segment_hash_by_ctx_name(&too_many_dotdots_other_file, "$")
	);
}

#[test]
fn inlined_qrl_uses_identifier_reference_when_hoisted() {
	let res = test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const App = component$(() => {
	return <div>Hello</div>;
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		transpile_ts: true,
		transpile_jsx: true,
		snapshot: false,
		..TestInput::default()
	});

	assert!(res.is_ok(), "Transform should succeed");
	let output = res.unwrap();

	let combined_code = output
		.modules
		.iter()
		.map(|module| module.code.as_str())
		.collect::<Vec<_>>()
		.join("\n");
	let compact_code: String = combined_code
		.chars()
		.filter(|c| !c.is_whitespace())
		.collect();

	assert!(
		compact_code.contains("_noopQrl(\"App_component_"),
		"Expected _noopQrl with symbol name.\nGenerated code:\n{}",
		combined_code
	);
	assert!(
		compact_code.contains(".s(App_component_")
			|| compact_code.contains(".s(TestComponent_component_"),
		"Expected s (setRef) call with hoisted identifier.\nGenerated code:\n{}",
		combined_code
	);
}

#[test]
fn inlined_qrl_uses_identifier_reference_when_hoisted_snapshot() {
	test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const App = component$(() => {
	return <div>Hello</div>;
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		transpile_ts: true,
		transpile_jsx: true,
		snapshot: true,
		..TestInput::default()
	});
}

#[test]
fn inlined_qrl_after_ref_identifiers_forward_ref() {
	// Test that inlinedQrl items are moved to come AFTER their referenced identifiers
	// even in cases where they might initially appear before them (e.g., third-party libs)
	let res = test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';
import { useAsyncQrl } from '@qwik.dev/core';

export const TestComponent = component$(() => {
	// This should be hoisted with an identifier
	const asyncSig = useAsyncQrl$(async () => {
		return 42;
	});
	return <div>{asyncSig}</div>;
});
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		transpile_ts: true,
		transpile_jsx: true,
		snapshot: false,
		..TestInput::default()
	});

	assert!(res.is_ok(), "Transform should succeed");
	let output = res.unwrap();

	let combined_code = output
		.modules
		.iter()
		.map(|module| module.code.as_str())
		.collect::<Vec<_>>()
		.join("\n");

	// Verify that inlinedQrl uses an identifier reference
	let compact_code: String = combined_code
		.chars()
		.filter(|c| !c.is_whitespace())
		.collect();

	assert!(
		compact_code.contains("_noopQrl(\"TestComponent_component_"),
		"Expected _noopQrl with symbol name.\nGenerated code:\n{}",
		combined_code
	);
	assert!(
		compact_code.contains(".s(App_component_")
			|| compact_code.contains(".s(TestComponent_component_"),
		"Expected s (setRef) call with hoisted identifier.\nGenerated code:\n{}",
		combined_code
	);
}

// Non-snapshot tests for generalized q:p/q:ps
#[test]
fn loop_iteration_vars_with_params() {
	// Test that loop iteration variables with event handlers work correctly
	let res = test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const Test = component$(() => {
	return (
		<div>
			{['a', 'b', 'c'].map((item, index) => (
				<button onClick$={() => console.log(item, index)}>
					{item}
				</button>
			))}
		</div>
	);
});
"#
		.to_string(),
		snapshot: false,
		..TestInput::default()
	});

	assert!(
		res.is_ok(),
		"Transform should succeed for loop with handlers"
	);
	let output = res.unwrap();

	// Verify modules were generated
	assert!(!output.modules.is_empty(), "Should have generated modules");

	// The transform should produce valid output without errors
	let entry_module = output.modules.iter().find(|m| m.is_entry);
	assert!(entry_module.is_some(), "Should have entry module");
}

#[test]
fn import_resolution_without_fallback() {
	// Test that imports are resolved correctly from explicit_imports without symbol-only fallbacks
	let res = test_input!(TestInput {
		code: r#"
import { signal, component$ } from '@qwik.dev/core';

export const Test = component$(() => {
	const sig = signal(0);
	return <div>{sig}</div>;
});
"#
		.to_string(),
		snapshot: false,
		..TestInput::default()
	});

	assert!(
		res.is_ok(),
		"Transform should succeed with proper import resolution"
	);
	let output = res.unwrap();

	// Verify all generated modules are valid
	for module in &output.modules {
		assert!(
			!module.code.is_empty(),
			"Module {} should have code",
			module.path
		);
		// Should not have symbol-only import mismatches that cause no-op fallbacks
	}
}

#[test]
fn should_work() {
	test_input!(TestInput {
		code: r#"
		import { component$ } from "@qwik.dev/core";
		import { globalAction$ } from "@qwik.dev/router";

		export const useSecretAction = globalAction$(
			async (payload) => console.log(payload) || 'hi'
		);

		export const SecretForm = component$(() => {
			const action = useSecretAction();
			return <div>{action.value}</div>
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn moves_captures_when_possible() {
	test_input!(TestInput {
		code: r#"
		import { component$, useSignal, $ } from "@qwik.dev/core";

		export const Test = component$(() => {
			const sig = useSignal(0);
			const foo = useSignal('foo');
			const bar = useSignal('bar');
			return <button onClick$={() => sig.value++} onDblClick$={() => foo.value+=sig.value} onHover$={() => sig.value+=bar.value}>{sig}</button>;
		});
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn fun_with_scopes() {
	let res = test_input!(TestInput {
		code: r#"
		import { inlinedQrl,$,component$,jsx,useStylesScoped$ } from '@qwik.dev/core';
			export default () => {
				const serverFnHash = globalThis.foo();
				const data = globalThis.bar();
				const qrl = inlinedQrl(null, serverFnHash, data.slice(1));
				console.log(qrl);
			}

			function Lifecycle(props, key, flags) {
				return component$(() => {
					return /* @__PURE__ */ jsx(Slot, {});
				})(props, key, flags);
			}
			export const rawFn = (event, element) => {
						handleFieldEvent(form, field, name, event, element, [
							"touched",
							"input"
						], getElementInput(element, field, type));
					}
			export function Field({ children, name, type, ...props }) {
				const { of: form } = props;
				const field = getFieldStore(form, name);
				return /* @__PURE__ */ jsx(Lifecycle, {
					store: field,
					...props,
					children: children(field, {
						name,
						autoFocus: isServer && !!field.error,
						ref: $((element) => {
							field.internal.elements.push(element);
						}),
						onInput$: $(rawFn),
					})
				}, name);
			}

			const STYLE_RED = `.container {background-color: red;}`;
			describe.each([])('$render.name: useStylesScoped', ({ render }) => {

				it('should render object style', async () => {
					const StyledComponent = component$(() => {
						const stylesScopedData = useStylesScoped$(STYLE_RED);
						const store = useStore({
							count: 10,
						});

						return (
							<button class={['container', `count-${store.count}`]} onClick$={() => store.count++}>
								Hello world
							</button>
						);
					});

					const { vNode, getStyles, document } = await render(<StyledComponent />, { debug });
				})
			})

			"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		is_server: Some(true),
		entry_strategy: EntryStrategy::Hoist,
		..TestInput::default()
	});
	// segments are drained and inlined for Hoist strategy
	assert!(res.is_ok());
}

#[test]
fn hmr() {
	test_input!(TestInput {
		code: r#"
		import { component$, component$, $ } from "@qwik.dev/core";

		export const TestGetsHmr = component$(() => {
			return <div>Test</div>;
		});
		export const TestNoHmr = componentQrl($(() => {
			return <div>Test</div>;
		}));
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		mode: EmitMode::Hmr,
		..TestInput::default()
	});
}

#[test]
fn example_lib_mode() {
	test_input!(TestInput {
		code: r#"
import { $, component$, server$, useStyle$, useTask$, useSignal } from '@qwik.dev/core';

export const Works = component$((props) => {
	useStyle$(STYLES);
	const text = 'hola';
	const sig = useSignal('hola');
	useTask$(() => {
		console.log(sig.value, text);
	});
	return (
		<div onClick$={server$(() => console.log('in server', sig.value, text))}></div>
	);
});

const STYLES = '.class {}';
"#
		.to_string(),
		mode: EmitMode::Lib,
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn lib_mode_inline_expressions() {
	let res = test_input!(TestInput {
		code: r#"
import { component$ } from '@qwik.dev/core';

export const App = component$(() => {
	return <div>Hello</div>;
});
"#
		.to_string(),
		mode: EmitMode::Lib,
		entry_strategy: EntryStrategy::Hoist,
		transpile_ts: true,
		transpile_jsx: true,
		snapshot: false,
		..TestInput::default()
	});

	assert!(res.is_ok(), "Transform should succeed");
	let output = res.unwrap();

	let combined_code = output
		.modules
		.iter()
		.map(|module| module.code.as_str())
		.collect::<Vec<_>>()
		.join("\n");
	let compact_code: String = combined_code
		.chars()
		.filter(|c| !c.is_whitespace())
		.collect();

	// In lib mode, inlinedQrl should have inline expressions, not extracted identifiers
	assert!(
		compact_code.contains("inlinedQrl(("),
		"Expected inlinedQrl first arg to be an inline function expression in lib mode.\nGenerated code:\n{}",
		combined_code
	);
	// No _captures should be used
	assert!(
		!compact_code.contains("_captures"),
		"Expected no _captures in lib mode output.\nGenerated code:\n{}",
		combined_code
	);
}

#[test]
fn inlined_qrl_preserves_captures() {
	// Simulates lib-preprocessed code being processed by the app optimizer.
	// The inner inlinedQrl has 5 captures, including variables defined via
	// the outer function's _captures destructuring.
	let res = test_input!(TestInput {
		code: r#"
import { componentQrl, inlinedQrl, useTaskQrl, useSignal, _captures } from '@qwik.dev/core';

export function qwikifyQrl(reactCmp$, opts) {
	return componentQrl(inlinedQrl((props) => {
		const opts2 = _captures[0], reactCmp$2 = _captures[1];
		const hostRef = useSignal();
		const signal = useSignal();
		const text = 'hello';
		useTaskQrl(inlinedQrl(async ({ track }) => {
			const hostRef2 = _captures[0], reactCmp$3 = _captures[1], opts3 = _captures[2], signal2 = _captures[3], text2 = _captures[4];
			track(signal2);
			console.log(hostRef2, reactCmp$3, opts3, text2);
		}, "s_inner123", [hostRef, reactCmp$2, opts2, signal, text]));
	}, "s_outer456", [opts, reactCmp$]));
}
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		minify: MinifyMode::Simplify,
		transpile_ts: false,
		transpile_jsx: false,
		snapshot: false,
		mode: EmitMode::Prod,
		is_server: Some(true),
		..TestInput::default()
	});

	assert!(res.is_ok(), "Transform should succeed: {:?}", res.err());
	let output = res.unwrap();

	let combined_code = output
		.modules
		.iter()
		.map(|module| module.code.as_str())
		.collect::<Vec<_>>()
		.join("\n");

	// The inner inlinedQrl must preserve all 5 captures
	// Find the inner QRL call with captures (format: q_s_inner123.w([captures])
	// or inlinedQrl(ref, "s_inner123", [captures]))
	let search = combined_code
		.find("q_s_inner123.w(")
		.or_else(|| combined_code.find("\"s_inner123\""))
		.expect(&format!(
			"Should find s_inner123 call in output.\nGenerated code:\n{}",
			combined_code
		));

	// Find the captures array (the [...] argument)
	let after_hash = &combined_code[search..];
	let bracket_start = after_hash.find('[').expect(&format!(
		"Should find captures array for s_inner123.\nGenerated code:\n{}",
		combined_code
	));
	let bracket_end = after_hash[bracket_start..]
		.find(']')
		.expect("Should find end of captures array");
	let captures_str = &after_hash[bracket_start + 1..bracket_start + bracket_end];

	// Count commas to get number of captures (N commas = N+1 elements)
	let capture_count = if captures_str.trim().is_empty() {
		0
	} else {
		captures_str.split(',').count()
	};

	assert_eq!(
		capture_count, 5,
		"Inner inlinedQrl should have exactly 5 captures, but found {}.\nCaptures: '{}'\nFull code:\n{}",
		capture_count, captures_str, combined_code
	);

	// Verify specific capture names are present
	for name in &["hostRef", "reactCmp$2", "opts2", "signal", "text"] {
		assert!(
			captures_str.contains(name),
			"Capture '{}' should be present in inner captures array.\nCaptures: '{}'\nFull code:\n{}",
			name, captures_str, combined_code
		);
	}
}

#[test]
fn inlined_qrl_preserves_destructured_captures() {
	// Simulates a library .qwik.mjs file being processed by the app optimizer in SSR dev mode.
	// The library has destructured useCustomSignal() return value, and inner inlinedQrl captures
	// reference the destructured bindings. transform_props_destructuring must not collapse
	// the destructuring because it would break the explicit captures.
	let res = test_input!(TestInput {
		code: r#"
import { componentQrl, inlinedQrl, useComputedQrl, useSignal, useTaskQrl, _captures, _jsxSorted } from '@qwik.dev/core';
import { useCustomSignal } from './use-custom-signal.qwik.mjs';

const MyComponent = componentQrl(inlinedQrl((props) => {
    const count = useSignal(0);
    const { openSig: isOpen } = useCustomSignal(props, { open: false });
    const label = useComputedQrl(inlinedQrl(() => {
        const count2 = _captures[0], isOpen2 = _captures[1];
        return count2.value + isOpen2.value;
    }, "MyComponent_component_label_useComputed_ABC123", [count, isOpen]));
    useTaskQrl(inlinedQrl(({ track }) => {
        const isOpen3 = _captures[0];
        track(() => isOpen3.value);
        console.log("isOpen changed:", isOpen3.value);
    }, "MyComponent_component_useTask_DEF456", [isOpen]));
    return _jsxSorted("div", null, {}, label.value, 0, null);
}, "MyComponent_component_MNO345"));

export { MyComponent };
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		minify: MinifyMode::None,
		transpile_ts: false,
		transpile_jsx: false,
		snapshot: false,
		mode: EmitMode::Dev,
		is_server: Some(true),
		..TestInput::default()
	});

	assert!(res.is_ok(), "Transform should succeed: {:?}", res.err());
	let output = res.unwrap();

	let combined_code = output
		.modules
		.iter()
		.map(|module| module.code.as_str())
		.collect::<Vec<_>>()
		.join("\n");

	// Verify isOpen survives transform_props_destructuring
	assert!(
		combined_code.contains("isOpen"),
		"isOpen should be present — transform_props_destructuring must not collapse destructured bindings in inlinedQrl function bodies.\nGenerated code:\n{}",
		combined_code
	);

	// Verify computed captures include both count and isOpen
	let computed_captures = combined_code
		.find("q_MyComponent_component_label_useComputed_ABC123.w(")
		.expect(&format!(
			"Should find computed QRL .w() call.\nGenerated code:\n{}",
			combined_code
		));
	let after = &combined_code[computed_captures..];
	let bracket_end = after.find("])").expect("Should find end of captures array");
	let captures_str = &after[..bracket_end + 1];
	assert!(
		captures_str.contains("count") && captures_str.contains("isOpen"),
		"Computed captures should include both count and isOpen.\nCaptures: '{}'\nFull code:\n{}",
		captures_str,
		combined_code
	);

	// Verify task captures include isOpen
	assert!(
		combined_code.contains("q_MyComponent_component_useTask_DEF456.w("),
		"Task QRL should have .w() captures with isOpen.\nGenerated code:\n{}",
		combined_code
	);
}

#[test]
fn lib_full_names_shortened_in_prod() {
	// Lib builds emit full symbol names (e.g. "Works_component_useTask_hash").
	// When a prod build consumes this lib, names should be shortened to "s_hash".
	let res = test_input!(TestInput {
		code: r#"
import { componentQrl, inlinedQrl, useTaskQrl, _captures } from '@qwik.dev/core';

export const Works = componentQrl(inlinedQrl((props) => {
	const text = 'hola';
	useTaskQrl(inlinedQrl(() => {
		const text = _captures[0];
		console.log(text);
	}, "Works_component_useTask_pjo5U5Ikll0", [text]));
}, "Works_component_t45qL4vNGv0"));
"#
		.to_string(),
		entry_strategy: EntryStrategy::Hoist,
		minify: MinifyMode::Simplify,
		transpile_ts: false,
		transpile_jsx: false,
		snapshot: false,
		mode: EmitMode::Prod,
		..TestInput::default()
	});

	assert!(res.is_ok(), "Transform should succeed: {:?}", res.err());
	let output = res.unwrap();

	let combined_code = output
		.modules
		.iter()
		.map(|module| module.code.as_str())
		.collect::<Vec<_>>()
		.join("\n");

	// Prod should shorten to s_hash format
	assert!(
		combined_code.contains("s_pjo5U5Ikll0"),
		"Prod build should shorten inner QRL name to s_hash.\nGenerated code:\n{}",
		combined_code
	);
	assert!(
		combined_code.contains("s_t45qL4vNGv0"),
		"Prod build should shorten outer QRL name to s_hash.\nGenerated code:\n{}",
		combined_code
	);
	// Full names should NOT appear in prod output
	assert!(
		!combined_code.contains("Works_component"),
		"Prod build should not contain full symbol names.\nGenerated code:\n{}",
		combined_code
	);
}

#[test]
fn should_not_inline_exported_var_into_segment() {
	test_input!(TestInput {
		code: r#"
		import { wrapperFn, getEnv } from 'utils';
		import { formAction$, valiForm$ } from 'forms';

		const flagEnabled = getEnv().PUBLIC_FEATURE;
		export const FeatureSchema = wrapperFn(flagEnabled);

		export const featureAction = formAction$(async (requestEvent) => {
			return {
				status: 'success',
			};
		}, valiForm$(FeatureSchema));
		"#
		.to_string(),
		transpile_ts: true,
		transpile_jsx: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_auto_export_var_shadowed_in_catch() {
	test_input!(TestInput {
		code: r#"
import { formAction$ } from 'forms';
import { translate } from 'i18n';

const t = translate();
export const action = formAction$((data) => {
  try {
    return { status: 'success' };
  } catch (err) {
    const t = translate();
    return { status: 'error', message: t('error-key') };
  }
});
		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_auto_export_var_shadowed_in_do_while() {
	test_input!(TestInput {
		code: r#"
import { formAction$ } from 'forms';
const x = 'module-level';
export const action = formAction$((data) => {
  let i = 0;
  do {
    const x = 'shadowed';
    i++;
  } while (i < 3);
  return {};
});
		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_auto_export_var_shadowed_in_switch() {
	test_input!(TestInput {
		code: r#"
import { formAction$ } from 'forms';
const x = 'module-level';
export const action = formAction$((data) => {
  switch (data.kind) {
    case 'a': {
      const x = 'shadowed-in-block';
      return x;
    }
    case 'b': {
      const x = 'shadowed-in-case';
      return x;
    }
  }
  return x;
});
		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_not_auto_export_var_shadowed_in_labeled_block() {
	test_input!(TestInput {
		code: r#"
import { formAction$ } from 'forms';
const x = 'module-level';
export const action = formAction$((data) => {
  label: {
    const x = 'shadowed';
    break label;
  }
  return {};
});
		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_keep_module_level_var_used_in_both_main_and_qrl() {
	// Minimal repro: module-level variable used both in main module scope
	// AND inside a $() closure. The optimizer must keep (or re-export) the
	// variable in the main module — moving it only to the chunk is a bug.
	test_input!(TestInput {
		code: r#"
import { $, isServer } from '@qwik.dev/core';

const state = {
  counter: 0,
  id: Math.random().toString(36).slice(2, 8),
};

console.log('Module init, id:', state.id);

export function doSomething() {
  if (!isServer) {
    state.counter++;
  }
}

export function useHook() {
  const fn = $(() => {
    state.counter++;
    console.log(state.id);
  });
  return fn;
}

		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_auto_export_shared_let_kept_in_parent() {
	// A module-scope `let` that is referenced by a segment AND still needed in the
	// main module stays in the parent and is auto-exported as a normal binding.
	// Segments become separate ES modules, so mutations from a segment won't be
	// visible in the parent — that's the developer's responsibility.
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';

let counter = 0;

export function bumpFromRoot() {
  counter++;
}

export const readFromSegment = $(() => {
  console.log(counter);
});
		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_preserve_let_when_migrated_into_segment() {
	// A module-scope `let` that is exclusive to a single segment is moved into that
	// segment; its declaration kind must be preserved so mutation inside the
	// segment still works.
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';

let counter = 0;

export const bump = $(() => {
  counter++;
  return counter;
});
		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_keep_non_migrated_binding_from_shared_destructuring_declarator() {
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';

const { a, b } = {
  a: 1,
  b: 2,
};

console.log('root', b);

export const handler = $(() => {
  console.log('qrl', a);
});

		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_keep_root_var_used_by_export_decl_and_qrl() {
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';

const shared = {
  id: 'abc',
};

export const exportedValue = shared.id;

export const handler = $(() => {
  console.log(shared.id);
});

		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_migrate_destructured_binding_with_imported_dependency() {
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';
import { source } from 'lib';

const { a } = source;

export const handler = $(() => {
  console.log(a);
});

		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_keep_non_migrated_binding_from_shared_array_destructuring_declarator() {
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';

const [a, b] = [1, 2];

console.log('root', b);

export const handler = $(() => {
  console.log('qrl', a);
});

		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_keep_non_migrated_binding_from_shared_destructuring_with_default() {
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';

const { a = 1, b } = { b: 2 };

console.log('root', b);

export const handler = $(() => {
  console.log('qrl', a);
});

		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_keep_non_migrated_binding_from_shared_destructuring_with_rest() {
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';

const { a, ...rest } = { a: 1, b: 2, c: 3 };

console.log('root', rest.b, rest.c);

export const handler = $(() => {
  console.log('qrl', a);
});

		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn should_keep_root_var_used_by_exported_function_and_qrl() {
	test_input!(TestInput {
		code: r#"
import { $ } from '@qwik.dev/core';

const shared = {
  id: 'abc',
};

export function readShared() {
  return shared.id;
}

export const handler = $(() => {
  console.log(shared.id);
});

		"#
		.to_string(),
		transpile_jsx: true,
		transpile_ts: true,
		..TestInput::default()
	});
}

#[test]
fn worker_qrl_segment_dev_snapshot() {
	test_input!(TestInput {
		code: r#"
import { worker$ } from '@qwik.dev/core';

export const runInWorker = worker$(() => 'hello');
"#
		.to_string(),
		filename: "src/routes/index.tsx".into(),
		mode: EmitMode::Dev,
		snapshot: true,
		..TestInput::default()
	});
}

#[test]
fn worker_qrl_segment_prod_snapshot() {
	test_input!(TestInput {
		code: r#"
import { worker$ } from '@qwik.dev/core';

export const runInWorker = worker$(() => 'hello');
"#
		.to_string(),
		filename: "src/routes/index.tsx".into(),
		mode: EmitMode::Prod,
		snapshot: true,
		..TestInput::default()
	});
}

#[test]
fn worker_qrl_direct_event_dev_snapshot() {
	test_input!(TestInput {
		code: r#"
import { component$, useSignal } from '@qwik.dev/core';
import { worker$ } from '@qwik.dev/core/worker';

export default component$(() => {
  const count = useSignal(0);
  return <button onClick$={worker$(() => count.value++)}>Move work to worker</button>;
});
"#
		.to_string(),
		filename: "src/routes/index.tsx".into(),
		mode: EmitMode::Dev,
		transpile_jsx: true,
		transpile_ts: true,
		snapshot: true,
		..TestInput::default()
	});
}

impl TestInput {
	pub fn default() -> Self {
		Self {
			filename: "test.tsx".to_string(),
			dev_path: None,
			src_dir: "/user/qwik/src/".to_string(),
			root_dir: None,
			code: "/user/qwik/src/".to_string(),
			entry_strategy: EntryStrategy::Segment,
			minify: MinifyMode::Simplify,
			transpile_ts: false,
			transpile_jsx: false,
			preserve_filenames: false,
			explicit_extensions: false,
			snapshot: true,
			mode: EmitMode::Test,
			scope: None,
			core_module: None,
			reg_ctx_name: None,
			strip_exports: None,
			strip_ctx_name: None,
			strip_event_handlers: false,
			is_server: None,
		}
	}
}

#[test]
fn should_preserve_non_ident_explicit_captures() {
	let res = test_input!(TestInput {
		code: r#"
import { _captures, inlinedQrl } from '@qwik.dev/core';

const left = 1;
const right = 2;

export const task = inlinedQrl(() => {
	const left = _captures[0];
	const middle = _captures[1];
	const right = _captures[2];
	return middle ? left : right;
}, 'task', [left, true, right]);
"#
		.to_string(),
		mode: EmitMode::Dev,
		..TestInput::default()
	});

	// check to make sure no snapshot regression in captures
	let output = res.unwrap();
	let entry_module = output
		.modules
		.iter()
		.find(|m| m.segment.is_none())
		.expect("entry module not found");
	let compact_code: String = entry_module
		.code
		.chars()
		.filter(|c| !c.is_whitespace())
		.collect();

	assert!(
		compact_code.contains(".w([left,true,right])"),
		"expected transformed capture array [left, true, right] to be preserved, got:\n{}",
		entry_module.code
	);
}
