Skip to content

Commit 2d9bc23

Browse files
committed
test(LGT-167): Update test suite after rework
1 parent df140d0 commit 2d9bc23

File tree

8 files changed

+653
-9
lines changed

8 files changed

+653
-9
lines changed

tests/LinguistTest.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,13 @@ function cleanUp(): void
221221
]),
222222
]);
223223

224-
$this->artisan('linguist:sync')
224+
$this->artisan('linguist:sync --mode=sync')
225225
->assertSuccessful()
226-
->expectsOutput('Syncing translations from Linguist...')
227-
->expectsOutput('Translations synced successfully.');
226+
->expectsOutputToContain('Starting linguist sync...')
227+
->expectsOutputToContain('Sync completed');
228228

229229
$languages->each(function (string $language) {
230-
assertFileExists(lang_path("$language/project.json"));
230+
assertFileExists(lang_path("$language/linguist.json"));
231231
});
232232
});
233233

@@ -237,7 +237,7 @@ function cleanUp(): void
237237

238238
$this->artisan('linguist:sync')
239239
->assertFailed()
240-
->expectsOutput('The linguist project is not available');
240+
->expectsOutputToContain('Linguist is not configured');
241241
});
242242

243243
test('artisan command fails when token is not configured', function () {
@@ -246,7 +246,7 @@ function cleanUp(): void
246246

247247
$this->artisan('linguist:sync')
248248
->assertFailed()
249-
->expectsOutput('The linguist token is not available');
249+
->expectsOutputToContain('Linguist is not configured');
250250
});
251251

252252
test('artisan command fails when no languages are activated', function () {
@@ -261,7 +261,7 @@ function cleanUp(): void
261261
]),
262262
]);
263263

264-
$this->artisan('linguist:sync')
264+
$this->artisan('linguist:sync --mode=pull')
265265
->assertFailed()
266-
->expectsOutput('No languages are activated in your Linguist project.');
266+
->expectsOutputToContain('No languages are activated in your Linguist project.');
267267
});

tests/Pest.php

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,101 @@
11
<?php
22

33
use Hyperlinkgroup\Linguist\Tests\TestCase;
4+
use Illuminate\Support\Facades\File;
5+
use Illuminate\Support\Facades\Http;
46

5-
uses(TestCase::class)->in(__DIR__);
7+
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
8+
uses(TestCase::class)->in('.');
9+
10+
/**
11+
* Configure valid Linguist settings for testing.
12+
*/
13+
function configureValidLinguistConfig(string $project = 'test-project', string $token = 'test-token'): void
14+
{
15+
config([
16+
'linguist.project' => $project,
17+
'linguist.token' => $token,
18+
'linguist.url' => 'https://api.linguist.eu',
19+
'linguist.temporary_directory' => 'tmp/translations',
20+
]);
21+
}
22+
23+
/**
24+
* Fake HTTP responses for common Linguist API endpoints.
25+
*/
26+
function fakeLinguistApi(array $responses = []): void
27+
{
28+
Http::preventStrayRequests();
29+
30+
$defaultResponses = [
31+
'https://api.linguist.eu/projects/*/languages' => Http::response([
32+
'data' => ['EN', 'DE'],
33+
]),
34+
'https://api.linguist.eu/projects/*/export/json/*' => Http::response([
35+
'url' => 'https://api.linguist.eu/export/test-export-uuid',
36+
]),
37+
'https://api.linguist.eu/export/*' => Http::response([
38+
'hello' => 'Hello World',
39+
'goodbye' => 'Goodbye World',
40+
]),
41+
];
42+
43+
Http::fake(array_merge($defaultResponses, $responses));
44+
}
45+
46+
/**
47+
* Create a mock HTTP response for project list endpoint.
48+
*/
49+
function fakeProjectListResponse(array $projects = []): void
50+
{
51+
$defaultProjects = [
52+
['id' => 1, 'slug' => 'project-one', 'name' => 'Project One'],
53+
['id' => 2, 'slug' => 'project-two', 'name' => 'Project Two'],
54+
];
55+
56+
Http::fake([
57+
'https://api.linguist.eu/projects' => Http::response([
58+
'data' => empty($projects) ? $defaultProjects : $projects,
59+
]),
60+
]);
61+
}
62+
63+
/**
64+
* Clean up test directories and files.
65+
*/
66+
function cleanTranslationFiles(): void
67+
{
68+
if (File::exists(lang_path())) {
69+
collect(File::files(lang_path(), true))->each(function (SplFileInfo $file) {
70+
File::delete($file->getPathname());
71+
});
72+
73+
File::deleteDirectory(lang_path());
74+
}
75+
76+
if (File::exists(storage_path('tmp/translations'))) {
77+
File::deleteDirectory(storage_path('tmp/translations'));
78+
}
79+
80+
if (File::exists(storage_path('tmp'))) {
81+
File::deleteDirectory(storage_path('tmp'));
82+
}
83+
}
84+
85+
/**
86+
* Create test translation files.
87+
*/
88+
function createTestTranslationFiles(string $project, array $languages = ['EN', 'DE']): void
89+
{
90+
File::ensureDirectoryExists(lang_path());
91+
92+
foreach ($languages as $language) {
93+
File::put(
94+
lang_path(strtolower($language) . '.json'),
95+
json_encode([
96+
'hello' => "Hello in {$language}",
97+
'goodbye' => "Goodbye in {$language}",
98+
], JSON_PRETTY_PRINT)
99+
);
100+
}
101+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
use Hyperlinkgroup\Linguist\Actions\CollectLocalTranslations;
4+
use Illuminate\Support\Facades\File;
5+
6+
afterEach(function () {
7+
cleanTranslationFiles();
8+
});
9+
10+
test('action collects translations from local directories', function () {
11+
File::ensureDirectoryExists(lang_path('DE'));
12+
File::ensureDirectoryExists(lang_path('EN'));
13+
File::ensureDirectoryExists(lang_path('spark'));
14+
15+
File::put(lang_path('DE/linguist.json'), json_encode([
16+
'managed' => 'Managed DE',
17+
], JSON_PRETTY_PRINT));
18+
File::put(lang_path('de.json'), json_encode([
19+
'hello' => 'Hallo',
20+
], JSON_PRETTY_PRINT));
21+
File::put(lang_path('EN/linguist.json'), json_encode([
22+
'managed' => 'Managed EN',
23+
], JSON_PRETTY_PRINT));
24+
File::put(lang_path('spark/en.json'), json_encode([
25+
'goodbye' => 'Bye from spark',
26+
], JSON_PRETTY_PRINT));
27+
28+
$action = new CollectLocalTranslations();
29+
$translations = $action->handle('test-project');
30+
31+
expect($translations)->toHaveKeys(['EN', 'DE'])
32+
->and($translations['EN'])->toHaveKeys(['managed', 'goodbye'])
33+
->and($translations['DE'])->toHaveKeys(['managed', 'hello']);
34+
});
35+
36+
test('action detects available languages', function () {
37+
File::ensureDirectoryExists(lang_path('EN'));
38+
File::ensureDirectoryExists(lang_path('spark'));
39+
40+
File::put(lang_path('EN/linguist.json'), json_encode(['managed' => 'Managed EN'], JSON_PRETTY_PRINT));
41+
File::put(lang_path('spark/de.json'), json_encode(['hello' => 'Hallo'], JSON_PRETTY_PRINT));
42+
43+
$action = new CollectLocalTranslations();
44+
$languages = $action->detectLanguages();
45+
46+
expect($languages)->toHaveCount(2)
47+
->and($languages)->toContain('EN')
48+
->and($languages)->toContain('DE');
49+
});
50+
51+
test('action parses nested translation files', function () {
52+
File::ensureDirectoryExists(lang_path());
53+
54+
File::put(
55+
lang_path('en.json'),
56+
json_encode([
57+
'user' => [
58+
'profile' => [
59+
'title' => 'User Profile',
60+
],
61+
],
62+
'hello' => 'Hello World',
63+
], JSON_PRETTY_PRINT)
64+
);
65+
66+
$action = new CollectLocalTranslations();
67+
$translations = $action->getTranslationsForLanguage('EN');
68+
69+
expect($translations)->toHaveKey('user.profile.title')
70+
->and($translations['user.profile.title'])->toBe('User Profile')
71+
->and($translations['hello'])->toBe('Hello World');
72+
});
73+
74+
test('action writes translations to file', function () {
75+
$action = new CollectLocalTranslations();
76+
77+
$translations = [
78+
'hello' => 'Hello World',
79+
'user.profile.title' => 'User Profile',
80+
];
81+
82+
$success = $action->writeTranslations('EN', 'test-project', $translations);
83+
84+
expect($success)->toBeTrue();
85+
86+
$filePath = lang_path('EN/linguist.json');
87+
expect(File::exists($filePath))->toBeTrue();
88+
89+
$content = json_decode(File::get($filePath), true);
90+
expect($content)->toHaveKey('hello')
91+
->and($content['hello'])->toBe('Hello World')
92+
->and($content)->toHaveKey('user')
93+
->and($content['user']['profile']['title'])->toBe('User Profile');
94+
});
95+
96+
test('action returns empty array for non-existent language', function () {
97+
$action = new CollectLocalTranslations();
98+
$translations = $action->getTranslationsForLanguage('XX', 'nonexistent');
99+
100+
expect($translations)->toBe([]);
101+
});
102+
103+
test('linguist managed keys override non linguist keys on collision', function () {
104+
File::ensureDirectoryExists(lang_path('EN'));
105+
File::ensureDirectoryExists(lang_path('spark'));
106+
107+
File::put(lang_path('en.json'), json_encode([
108+
'hello' => 'Hello from base',
109+
], JSON_PRETTY_PRINT));
110+
111+
File::put(lang_path('spark/en.json'), json_encode([
112+
'hello' => 'Hello from spark',
113+
], JSON_PRETTY_PRINT));
114+
115+
File::put(lang_path('EN/linguist.json'), json_encode([
116+
'hello' => 'Hello from linguist',
117+
], JSON_PRETTY_PRINT));
118+
119+
$action = new CollectLocalTranslations();
120+
$translations = $action->handle('test-project');
121+
$english = $action->getTranslationsForLanguage('EN', 'test-project');
122+
123+
expect($translations['EN']['hello'])->toBe('Hello from linguist')
124+
->and($english['hello'])->toBe('Hello from linguist');
125+
});
126+
127+
test('action can be run as an invokable', function () {
128+
createTestTranslationFiles('test-project', ['EN']);
129+
130+
$action = new CollectLocalTranslations();
131+
$translations = $action('test-project');
132+
133+
expect($translations)->toHaveKey('EN');
134+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
use Hyperlinkgroup\Linguist\Actions\PersistLinguistConfig;
4+
use Illuminate\Support\Facades\File;
5+
6+
afterEach(function () {
7+
$envPath = base_path('.env');
8+
if (File::exists($envPath)) {
9+
File::delete($envPath);
10+
}
11+
});
12+
13+
test('action persists config values to env file', function () {
14+
$action = new PersistLinguistConfig();
15+
16+
$results = $action->handle([
17+
'token' => 'test-token-123',
18+
'project' => 'my-project',
19+
'url' => 'https://api.linguist.eu',
20+
]);
21+
22+
expect($results)->toHaveKey('token')
23+
->and($results['token'])->toBeTrue()
24+
->and($results['project'])->toBeTrue();
25+
26+
$envContent = File::get(base_path('.env'));
27+
expect($envContent)->toContain('LINGUIST_TOKEN=test-token-123')
28+
->and($envContent)->toContain('LINGUIST_PROJECT=my-project');
29+
});
30+
31+
test('action escapes values with special characters', function () {
32+
$action = new PersistLinguistConfig();
33+
34+
$action->handle([
35+
'token' => 'token with spaces and #hash',
36+
]);
37+
38+
$envContent = File::get(base_path('.env'));
39+
expect($envContent)->toContain('"');
40+
});
41+
42+
test('action updates existing values', function () {
43+
$action = new PersistLinguistConfig();
44+
45+
// First write
46+
$action->handle(['token' => 'first-token']);
47+
48+
// Second write should update
49+
$action->handle(['token' => 'second-token']);
50+
51+
$envContent = File::get(base_path('.env'));
52+
expect($envContent)->toContain('LINGUIST_TOKEN=second-token')
53+
->and($envContent)->not->toContain('first-token');
54+
});
55+
56+
test('action masks token for display', function () {
57+
$action = new PersistLinguistConfig();
58+
config(['linguist.token' => 'test-token-1234']);
59+
60+
$masked = $action->getConfigSummary();
61+
62+
expect($masked['token'])->toContain('...');
63+
});
64+
65+
test('action can be run as an invokable', function () {
66+
$action = new PersistLinguistConfig();
67+
68+
$results = $action(['token' => 'invokable-token']);
69+
70+
expect($results['token'])->toBeTrue();
71+
});

0 commit comments

Comments
 (0)