Skip to content

Commit 4b41492

Browse files
committed
test: increase coverage and fix CI
1 parent f46a53d commit 4b41492

4 files changed

Lines changed: 314 additions & 7 deletions

File tree

.github/workflows/drupal-module.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
SIMPLETEST_DB: "sqlite://localhost/sites/default/files/db.sqlite"
5555

5656
steps:
57-
- uses: actions/checkout@v6
57+
- uses: actions/checkout@v4
5858

5959
- uses: shivammathur/setup-php@v2
6060
with:
@@ -99,29 +99,29 @@ jobs:
9999
chmod -R 777 drupal/web/sites/simpletest/browser_output
100100
101101
- name: Run PHPUnit
102-
if: ${{ !matrix.coverage || github.ref != 'refs/heads/master' }}
102+
if: ${{ !matrix.coverage }}
103103
run: |
104104
cd drupal/web
105105
../vendor/bin/phpunit -c core modules/contrib/jsonapi_frontend_webform/tests
106106
107107
- name: Run PHPUnit (coverage)
108-
if: ${{ matrix.coverage && github.ref == 'refs/heads/master' }}
108+
if: ${{ matrix.coverage }}
109109
run: |
110110
cd drupal/web
111111
../vendor/bin/phpunit -c core modules/contrib/jsonapi_frontend_webform/tests \
112112
--coverage-clover "$GITHUB_WORKSPACE/coverage.xml" \
113113
--coverage-filter modules/contrib/jsonapi_frontend_webform
114114
115115
- name: Upload coverage artifact
116-
if: ${{ matrix.coverage && github.ref == 'refs/heads/master' }}
116+
if: ${{ matrix.coverage }}
117117
uses: actions/upload-artifact@v4
118118
with:
119119
name: coverage-jsonapi_frontend_webform
120120
path: coverage.xml
121121
if-no-files-found: error
122122

123123
- name: Upload coverage to Codecov
124-
if: ${{ matrix.coverage && github.ref == 'refs/heads/master' }}
124+
if: ${{ matrix.coverage }}
125125
continue-on-error: true
126126
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de
127127
with:

.github/workflows/semgrep.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
security-events: write
1818

1919
steps:
20-
- uses: actions/checkout@v6
20+
- uses: actions/checkout@v4
2121

2222
- name: Run Semgrep
2323
run: |

codecov.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
coverage:
2+
status:
3+
project:
4+
default:
5+
target: 0.9
6+
threshold: 0.01
7+
patch:
8+
default:
9+
target: 0.9
10+
threshold: 0.01
11+

tests/src/Unit/WebformPathResolverTest.php

Lines changed: 297 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66

77
use Drupal\Core\Config\ConfigFactoryInterface;
88
use Drupal\Core\Extension\ModuleHandlerInterface;
9+
use Drupal\Core\Language\LanguageInterface;
910
use Drupal\Core\Language\LanguageManagerInterface;
1011
use Drupal\Core\Path\PathValidatorInterface;
1112
use Drupal\Core\Url;
1213
use Drupal\jsonapi_frontend\Service\PathResolverInterface;
1314
use Drupal\jsonapi_frontend_webform\Service\WebformPathResolver;
1415
use Drupal\path_alias\AliasManagerInterface;
1516
use PHPUnit\Framework\TestCase;
17+
use Symfony\Component\HttpFoundation\Request;
1618
use Symfony\Component\HttpFoundation\RequestStack;
1719

1820
/**
@@ -36,6 +38,21 @@ private function notFound(): array {
3638
];
3739
}
3840

41+
private function createConfigFactory(array $values): ConfigFactoryInterface {
42+
$config = new class($values) {
43+
public function __construct(private readonly array $values) {}
44+
45+
public function get(string $key): mixed {
46+
return $this->values[$key] ?? NULL;
47+
}
48+
};
49+
50+
$factory = $this->createMock(ConfigFactoryInterface::class);
51+
$factory->method('get')->with('jsonapi_frontend.settings')->willReturn($config);
52+
53+
return $factory;
54+
}
55+
3956
public function testReturnsInnerResultWhenAlreadyResolved(): void {
4057
$inner = $this->createMock(PathResolverInterface::class);
4158
$inner->expects(self::once())
@@ -88,6 +105,286 @@ public function testNoWebformModuleReturnsInnerResult(): void {
88105
$this->assertFalse($result['resolved']);
89106
}
90107

108+
public function testReturnsInnerResultWhenPathTooLong(): void {
109+
$inner = $this->createMock(PathResolverInterface::class);
110+
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
111+
112+
$module_handler = $this->createMock(ModuleHandlerInterface::class);
113+
$module_handler->method('moduleExists')->with('webform')->willReturn(TRUE);
114+
115+
$alias_manager = $this->createMock(AliasManagerInterface::class);
116+
$alias_manager->expects(self::never())->method('getPathByAlias');
117+
118+
$resolver = new WebformPathResolver(
119+
$inner,
120+
$alias_manager,
121+
$this->createMock(PathValidatorInterface::class),
122+
$this->createMock(LanguageManagerInterface::class),
123+
$module_handler,
124+
$this->createMock(ConfigFactoryInterface::class),
125+
$this->createMock(RequestStack::class),
126+
);
127+
128+
$result = $resolver->resolve(str_repeat('a', 2050), 'en');
129+
$this->assertFalse($result['resolved']);
130+
}
131+
132+
public function testResolvesWebformRouteUsingSiteDefaultLanguageFallbackAndRequestBaseUrl(): void {
133+
$inner = $this->createMock(PathResolverInterface::class);
134+
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
135+
136+
$module_handler = $this->createMock(ModuleHandlerInterface::class);
137+
$module_handler->method('moduleExists')->with('webform')->willReturn(TRUE);
138+
139+
$language = $this->createMock(LanguageInterface::class);
140+
$language->method('getId')->willReturn('en');
141+
142+
$language_manager = $this->createMock(LanguageManagerInterface::class);
143+
$language_manager->expects(self::once())->method('getDefaultLanguage')->willReturn($language);
144+
145+
$alias_manager = $this->createMock(AliasManagerInterface::class);
146+
$alias_manager->expects(self::once())
147+
->method('getPathByAlias')
148+
->with('/contact', 'en')
149+
->willReturn('/form/contact');
150+
$alias_manager->expects(self::once())
151+
->method('getAliasByPath')
152+
->with('/form/contact', 'en')
153+
->willReturn('');
154+
155+
$url = $this->createMock(Url::class);
156+
$url->method('access')->willReturn(TRUE);
157+
158+
$path_validator = $this->createMock(PathValidatorInterface::class);
159+
$path_validator->expects(self::once())->method('getUrlIfValid')->with('/form/contact')->willReturn($url);
160+
161+
$config_factory = $this->createConfigFactory([
162+
'resolver.langcode_fallback' => 'site_default',
163+
'drupal_base_url' => '',
164+
]);
165+
166+
$request_stack = $this->createMock(RequestStack::class);
167+
$request_stack->method('getCurrentRequest')->willReturn(Request::create('https://cms.example.com'));
168+
169+
$resolver = new WebformPathResolver(
170+
$inner,
171+
$alias_manager,
172+
$path_validator,
173+
$language_manager,
174+
$module_handler,
175+
$config_factory,
176+
$request_stack,
177+
);
178+
179+
$result = $resolver->resolve('contact/?utm=1#frag');
180+
181+
$this->assertTrue($result['resolved']);
182+
$this->assertSame('route', $result['kind']);
183+
$this->assertSame('/contact', $result['canonical']);
184+
$this->assertSame('https://cms.example.com/contact', $result['drupal_url']);
185+
}
186+
187+
public function testResolvesWebformRouteUsingCurrentLanguageFallback(): void {
188+
$inner = $this->createMock(PathResolverInterface::class);
189+
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
190+
191+
$module_handler = $this->createMock(ModuleHandlerInterface::class);
192+
$module_handler->method('moduleExists')->with('webform')->willReturn(TRUE);
193+
194+
$language = $this->createMock(LanguageInterface::class);
195+
$language->method('getId')->willReturn('fr');
196+
197+
$language_manager = $this->createMock(LanguageManagerInterface::class);
198+
$language_manager->expects(self::once())
199+
->method('getCurrentLanguage')
200+
->with(LanguageInterface::TYPE_CONTENT)
201+
->willReturn($language);
202+
203+
$alias_manager = $this->createMock(AliasManagerInterface::class);
204+
$alias_manager->expects(self::once())
205+
->method('getPathByAlias')
206+
->with('/contact', 'fr')
207+
->willReturn('/form/contact');
208+
$alias_manager->expects(self::once())
209+
->method('getAliasByPath')
210+
->with('/form/contact', 'fr')
211+
->willReturn('/contact');
212+
213+
$url = $this->createMock(Url::class);
214+
$url->method('access')->willReturn(TRUE);
215+
216+
$path_validator = $this->createMock(PathValidatorInterface::class);
217+
$path_validator->expects(self::once())->method('getUrlIfValid')->with('/form/contact')->willReturn($url);
218+
219+
$config_factory = $this->createConfigFactory([
220+
'resolver.langcode_fallback' => 'current',
221+
'drupal_base_url' => 'https://cms.example.com',
222+
]);
223+
224+
$resolver = new WebformPathResolver(
225+
$inner,
226+
$alias_manager,
227+
$path_validator,
228+
$language_manager,
229+
$module_handler,
230+
$config_factory,
231+
$this->createMock(RequestStack::class),
232+
);
233+
234+
$result = $resolver->resolve('/contact');
235+
236+
$this->assertTrue($result['resolved']);
237+
$this->assertSame('/contact', $result['canonical']);
238+
}
239+
240+
public function testReturnsInnerResultWhenInternalPathIsNotAWebformRoute(): void {
241+
$inner = $this->createMock(PathResolverInterface::class);
242+
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
243+
244+
$module_handler = $this->createMock(ModuleHandlerInterface::class);
245+
$module_handler->method('moduleExists')->with('webform')->willReturn(TRUE);
246+
247+
$alias_manager = $this->createMock(AliasManagerInterface::class);
248+
$alias_manager->expects(self::once())->method('getPathByAlias')->with('/contact', 'en')->willReturn('/node/1');
249+
$alias_manager->expects(self::never())->method('getAliasByPath');
250+
251+
$path_validator = $this->createMock(PathValidatorInterface::class);
252+
$path_validator->expects(self::never())->method('getUrlIfValid');
253+
254+
$resolver = new WebformPathResolver(
255+
$inner,
256+
$alias_manager,
257+
$path_validator,
258+
$this->createMock(LanguageManagerInterface::class),
259+
$module_handler,
260+
$this->createConfigFactory([]),
261+
$this->createMock(RequestStack::class),
262+
);
263+
264+
$result = $resolver->resolve('/contact', 'en');
265+
$this->assertFalse($result['resolved']);
266+
}
267+
268+
public function testReturnsInnerResultWhenAliasResolutionFails(): void {
269+
$inner = $this->createMock(PathResolverInterface::class);
270+
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
271+
272+
$module_handler = $this->createMock(ModuleHandlerInterface::class);
273+
$module_handler->method('moduleExists')->with('webform')->willReturn(TRUE);
274+
275+
$alias_manager = $this->createMock(AliasManagerInterface::class);
276+
$alias_manager->expects(self::once())->method('getPathByAlias')->willReturn(NULL);
277+
278+
$resolver = new WebformPathResolver(
279+
$inner,
280+
$alias_manager,
281+
$this->createMock(PathValidatorInterface::class),
282+
$this->createMock(LanguageManagerInterface::class),
283+
$module_handler,
284+
$this->createConfigFactory([]),
285+
$this->createMock(RequestStack::class),
286+
);
287+
288+
$result = $resolver->resolve('/contact', 'en');
289+
$this->assertFalse($result['resolved']);
290+
}
291+
292+
public function testReturnsInnerResultWhenUrlIsInvalid(): void {
293+
$inner = $this->createMock(PathResolverInterface::class);
294+
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
295+
296+
$module_handler = $this->createMock(ModuleHandlerInterface::class);
297+
$module_handler->method('moduleExists')->with('webform')->willReturn(TRUE);
298+
299+
$alias_manager = $this->createMock(AliasManagerInterface::class);
300+
$alias_manager->method('getPathByAlias')->with('/contact', 'en')->willReturn('/form/contact');
301+
302+
$path_validator = $this->createMock(PathValidatorInterface::class);
303+
$path_validator->method('getUrlIfValid')->with('/form/contact')->willReturn(NULL);
304+
305+
$resolver = new WebformPathResolver(
306+
$inner,
307+
$alias_manager,
308+
$path_validator,
309+
$this->createMock(LanguageManagerInterface::class),
310+
$module_handler,
311+
$this->createConfigFactory([]),
312+
$this->createMock(RequestStack::class),
313+
);
314+
315+
$result = $resolver->resolve('/contact', 'en');
316+
$this->assertFalse($result['resolved']);
317+
}
318+
319+
public function testReturnsInnerResultWhenAccessDenied(): void {
320+
$inner = $this->createMock(PathResolverInterface::class);
321+
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
322+
323+
$module_handler = $this->createMock(ModuleHandlerInterface::class);
324+
$module_handler->method('moduleExists')->with('webform')->willReturn(TRUE);
325+
326+
$alias_manager = $this->createMock(AliasManagerInterface::class);
327+
$alias_manager->method('getPathByAlias')->with('/contact', 'en')->willReturn('/form/contact');
328+
329+
$url = $this->createMock(Url::class);
330+
$url->method('access')->willReturn(FALSE);
331+
332+
$path_validator = $this->createMock(PathValidatorInterface::class);
333+
$path_validator->method('getUrlIfValid')->with('/form/contact')->willReturn($url);
334+
335+
$resolver = new WebformPathResolver(
336+
$inner,
337+
$alias_manager,
338+
$path_validator,
339+
$this->createMock(LanguageManagerInterface::class),
340+
$module_handler,
341+
$this->createConfigFactory([]),
342+
$this->createMock(RequestStack::class),
343+
);
344+
345+
$result = $resolver->resolve('/contact', 'en');
346+
$this->assertFalse($result['resolved']);
347+
}
348+
349+
public function testDrupalUrlFallsBackToPathWhenNoConfiguredBaseUrlAndNoRequest(): void {
350+
$inner = $this->createMock(PathResolverInterface::class);
351+
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
352+
353+
$module_handler = $this->createMock(ModuleHandlerInterface::class);
354+
$module_handler->method('moduleExists')->with('webform')->willReturn(TRUE);
355+
356+
$alias_manager = $this->createMock(AliasManagerInterface::class);
357+
$alias_manager->method('getPathByAlias')->with('/contact', 'en')->willReturn('/form/contact');
358+
$alias_manager->method('getAliasByPath')->with('/form/contact', 'en')->willReturn('/contact');
359+
360+
$url = $this->createMock(Url::class);
361+
$url->method('access')->willReturn(TRUE);
362+
363+
$path_validator = $this->createMock(PathValidatorInterface::class);
364+
$path_validator->method('getUrlIfValid')->with('/form/contact')->willReturn($url);
365+
366+
$config_factory = $this->createConfigFactory([
367+
'drupal_base_url' => '',
368+
]);
369+
370+
$request_stack = $this->createMock(RequestStack::class);
371+
$request_stack->method('getCurrentRequest')->willReturn(NULL);
372+
373+
$resolver = new WebformPathResolver(
374+
$inner,
375+
$alias_manager,
376+
$path_validator,
377+
$this->createMock(LanguageManagerInterface::class),
378+
$module_handler,
379+
$config_factory,
380+
$request_stack,
381+
);
382+
383+
$result = $resolver->resolve('/contact', 'en');
384+
$this->assertTrue($result['resolved']);
385+
$this->assertSame('/contact', $result['drupal_url']);
386+
}
387+
91388
public function testResolvesWebformRouteFromAlias(): void {
92389
$inner = $this->createMock(PathResolverInterface::class);
93390
$inner->expects(self::once())->method('resolve')->willReturn($this->notFound());
@@ -137,4 +434,3 @@ public function get(string $key): mixed {
137434
}
138435

139436
}
140-

0 commit comments

Comments
 (0)