Repository: GraphiteEditor/Graphite
File: editor/src/messages/tool/tool_messages/gradient_tool.rs (Rust)

Code before:
					editor_position: click_position,
					mouse_keys: MouseKeys::LEFT,
					scroll_delta: ScrollDelta::default(),
				},
				modifier_keys,
			})
			.await;

		// Check that a new stop has been added
		let updated_fills = get_fills(&mut editor).await;
		assert_eq!(updated_fills.len(), 1);
		let (updated_fill, _) = updated_fills.first().unwrap();
		let updated_gradient = updated_fill.as_gradient().unwrap();

		assert_eq!(updated_gradient.stops.len(), 3);

		let positions: Vec<f64> = updated_gradient.stops.iter().map(|(pos, _)| *pos).collect();
		assert!(
			positions.iter().any(|pos| (pos - 0.5).abs() < 0.1),
			"Expected to find a stop near position 0.5, but found: {:?}",
			positions
		);
	}

	#[tokio::test]
	async fn dragging_endpoint_sets_correct_point() {
		let mut editor = EditorTestUtils::create();
		editor.new_document().await;

		editor.drag_tool(ToolType::Rectangle, -5., -3., 100., 100., ModifierKeys::empty()).await;

		editor.select_primary_color(Color::GREEN).await;
		editor.select_secondary_color(Color::BLUE).await;

		editor.drag_tool(ToolType::Gradient, 0., 0., 100., 0., ModifierKeys::empty()).await;

		// Get the initial gradient state (should have 2 stops)
		let initial_fills = get_fills(&mut editor).await;
		assert_eq!(initial_fills.len(), 1);
		let (initial_fill, transform) = initial_fills.first().unwrap();
		let initial_gradient = initial_fill.as_gradient().unwrap();

		// Verify initial gradient endpoints
		let initial_start = transform.transform_point2(initial_gradient.start);
		let initial_end = transform.transform_point2(initial_gradient.end);
		assert!(initial_start.abs_diff_eq(DVec2::new(0., 0.), 1e-10));
		assert!(initial_end.abs_diff_eq(DVec2::new(100., 0.), 1e-10));

		editor.select_tool(ToolType::Gradient).await;

		// Simulate dragging the end point to a new position (100, 50)

Review comment (suggestion):
It might be useful to change the transform of the layer as well as to zoom the document. This actually confirms that it works in viewport space. You can do this with something like:
```rs
editor.handle_message(NavigationMessage::CanvasZoomSet { zoom_factor: 2.0 }).await;
editor.handle_message(GraphOperationMessage::TransformSet { ... }).await;
```

Code after:
					editor_position: click_position,
					mouse_keys: MouseKeys::LEFT,
					scroll_delta: ScrollDelta::default(),
				},
				modifier_keys,
			})
			.await;

		// Check that a new stop has been added
		let updated_fills = get_fills(&mut editor).await;
		assert_eq!(updated_fills.len(), 1);
		let (updated_fill, _) = updated_fills.first().unwrap();
		let updated_gradient = updated_fill.as_gradient().unwrap();

		assert_eq!(updated_gradient.stops.len(), 3);

		let positions: Vec<f64> = updated_gradient.stops.iter().map(|(pos, _)| *pos).collect();
		assert!(
			positions.iter().any(|pos| (pos - 0.5).abs() < 0.1),
			"Expected to find a stop near position 0.5, but found: {:?}",
			positions
		);
	}

	#[tokio::test]
	async fn dragging_endpoint_sets_correct_point() {
		let mut editor = EditorTestUtils::create();
		editor.new_document().await;

		editor.handle_message(NavigationMessage::CanvasZoomSet { zoom_factor: 2.0 }).await;

		editor.drag_tool(ToolType::Rectangle, -5., -3., 100., 100., ModifierKeys::empty()).await;

		let document = editor.active_document();
		let selected_layer = document.network_interface.selected_nodes().selected_layers(document.metadata()).next().unwrap();
		editor
			.handle_message(GraphOperationMessage::TransformSet {
				layer: selected_layer,
				transform: DAffine2::from_scale_angle_translation(DVec2::new(1.5, 0.8), 0.3, DVec2::new(10.0, -5.0)),
				transform_in: TransformIn::Local,
				skip_rerender: false,
			})
			.await;

		editor.select_primary_color(Color::GREEN).await;
		editor.select_secondary_color(Color::BLUE).await;

		editor.drag_tool(ToolType::Gradient, 0., 0., 100., 0., ModifierKeys::empty()).await;

		// Get the initial gradient state (should have 2 stops)
		let initial_fills = get_fills(&mut editor).await;