diff --git a/ecrire/tests/Urls/LiensOuvrantsTest.php b/ecrire/tests/Urls/LiensOuvrantsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..3b69e9bf54c7e25b3305622ee66e20d46c6fef19
--- /dev/null
+++ b/ecrire/tests/Urls/LiensOuvrantsTest.php
@@ -0,0 +1,83 @@
+<?php
+
+declare(strict_types=1);
+
+
+namespace Spip\Test\Urls;
+
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\TestCase;
+
+class LiensOuvrantsTest extends TestCase
+{
+	public static function setUpBeforeClass(): void {
+		find_in_path('./inc/utils.php', '', true);
+	}
+
+	#[DataProvider('providerAttendus')]
+	public function testAttendus($text) {
+		$link = liens_ouvrants($text);
+		$this->assertEquals('_blank', extraire_attribut($link, 'target'), sprintf('Lien ouvrant "%s" n’a pas target', $link));
+		$this->assertStringContainsString('noopener', extraire_attribut($link, 'rel'), sprintf('Lien ouvrant "%s" n’a pas noopener', $link));
+		$this->assertStringContainsString('noreferrer', extraire_attribut($link, 'rel'), sprintf('Lien ouvrant "%s" n’a pas noreferrer', $link));
+	}
+
+	public static function providerAttendus(): array {
+		return [
+			'lien spip #1' => [
+				'text' => '<a href="https://www.spip.net/" class="spip_out">link</a>',
+			],
+			'lien spip #2' => [
+				'text' => '<a href="https://www.spip.net/" class="spip_url">link</a>',
+			],
+		];
+	}
+
+	#[DataProvider('providerNonAttendus')]
+	public function testNonAttendus($text) {
+		$link = liens_ouvrants($text);
+		$this->assertNull(extraire_attribut($link, 'target'), sprintf('Lien "%s" a un target imprevu', $link));
+		$this->assertNull(extraire_attribut($link, 'rel'), sprintf('Lien "%s" a un rel imprevu', $link));
+	}
+
+	public static function providerNonAttendus(): array {
+		return [
+			'non spip #1' => [
+				'expected' => null,
+				'text' => '<a href="https://www.spip.net/">link</a>',
+			],
+			'non spip #2' => [
+				'expected' => null,
+				'text' => '<a href="https://www.spip.net/" class="spip_in">link</a>',
+			],
+			'non spip #3' => [
+				'expected' => null,
+				'text' => '<a href="https://www.spip.net/" class="spip_outside">link</a>',
+			],
+			'non spip #4' => [
+				'expected' => null,
+				'text' => '<a href="https://www.spip.net/" class="spip_urls">link</a>',
+			],
+		];
+	}
+
+
+	#[DataProvider('providerLiensOuvrantsPropre')]
+	public function testLiensOuvrantsPropre($text) {
+		$link = liens_ouvrants(propre($text));
+		$this->assertEquals('_blank', extraire_attribut($link, 'target'));
+		$this->assertStringContainsString('noopener', extraire_attribut($link, 'rel'));
+		$this->assertStringContainsString('noreferrer', extraire_attribut($link, 'rel'));
+	}
+
+	public static function providerLiensOuvrantsPropre(): array {
+		return [
+			'propre lien http' => [
+				'text' => 'Ceci est un lien [ouvrant->http://www.spip.net/ar] rondtudiou.',
+			],
+			'propre lien https' => [
+				'text' => 'Ceci est un lien [ouvrant->https://www.spip.net/ar] rondtudiou.',
+			]
+		];
+	}
+}
diff --git a/ecrire/tests/legacy/unit/filtres/liens_ouvrants.html b/ecrire/tests/legacy/unit/filtres/liens_ouvrants.html
deleted file mode 100644
index 9c354411a4ae2a0a8ae675ca8e0541489f08fa04..0000000000000000000000000000000000000000
--- a/ecrire/tests/legacy/unit/filtres/liens_ouvrants.html
+++ /dev/null
@@ -1,17 +0,0 @@
-[(#REM)
-
-	Teste le filtre |liens_ouvrants
-
-]
-
-
-#SET{texte,
-	Ceci est un lien [ouvrant->http://www.spip.net/ar] rondtudiou.
-}
-
-[(#GET{texte}|propre
-	|liens_ouvrants
-	|match{<a\s.*target=('|")_blank('|").*>}
-	|?{OK}
-	|sinon{#GET{texte}|propre})]
-