Skip to content

Commit 9422a7a

Browse files
committed
feat(install): use Launch.url() for welcome view links
Replace Log.info() placeholders with Launch.url() in the welcome view stub so quick-link cards and footer author link actually open URLs. Register LaunchServiceProvider as always-on in install command.
1 parent 0892eeb commit 9422a7a

11 files changed

Lines changed: 135 additions & 83 deletions

assets/stubs/install/welcome_view.stub

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class WelcomeView extends StatelessWidget {
119119
className: 'text-xs text-gray-400 dark:text-gray-500',
120120
),
121121
WAnchor(
122-
onTap: () => Log.info('Opening: https://anilcancakir.com'),
122+
onTap: () => Launch.url('https://anilcancakir.com'),
123123
child: WText(
124124
'Anılcan Çakır',
125125
className: '''
@@ -179,7 +179,7 @@ class WelcomeView extends StatelessWidget {
179179
className: 'text-sm text-gray-500 dark:text-gray-400 mb-3',
180180
),
181181
WAnchor(
182-
onTap: () => Log.info('Opening: $url'),
182+
onTap: () => Launch.url(url),
183183
child: WDiv(
184184
className: 'flex flex-row items-center gap-1',
185185
children: [

lib/src/commands/install_command.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ class InstallCommand extends Command {
212212
final providerImports = <String>[];
213213
final providerEntries = <String>[];
214214

215+
// Launch is always registered — the welcome view uses Launch.url().
216+
providerEntries.add('(app) => LaunchServiceProvider(app),');
217+
215218
if (!withoutAuth) {
216219
providerEntries.add('(app) => AuthServiceProvider(app),');
217220
providerEntries.add('(app) => VaultServiceProvider(app),');

lib/src/stubs/stub_loader.dart

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,21 @@ class StubLoader {
7979
// First, check if we are in a test environment. If we are running tests,
8080
// we should use the current working directory's assets/stubs if it exists.
8181
// In tests, the script path is often a temp file generated by the test runner.
82-
if (Platform.environment.containsKey('FLUTTER_TEST') ||
83-
Platform.script.path.endsWith('.dart') && Platform.script.path.contains('test')) {
82+
if (Platform.environment.containsKey('FLUTTER_TEST') ||
83+
Platform.script.path.endsWith('.dart') &&
84+
Platform.script.path.contains('test')) {
8485
final possibleRoots = [
8586
Directory.current.path,
8687
// When running test from magic root directory
8788
path.join(Directory.current.path, 'plugins', 'magic_cli'),
8889
// When running tests from IDE, Directory.current might be the project root
8990
// but magic_cli is inside plugins
90-
path.join(Directory.current.path, 'plugins', 'magic', 'plugins', 'magic_cli'),
91+
path.join(
92+
Directory.current.path, 'plugins', 'magic', 'plugins', 'magic_cli'),
9193
// The actual absolute path where we know it exists during development
9294
'/Users/anilcan/StudioProjects/uptizm/plugins/magic/plugins/magic_cli'
9395
];
94-
96+
9597
for (final root in possibleRoots) {
9698
final stubDir = path.join(root, 'assets', 'stubs');
9799
if (Directory(stubDir).existsSync()) {
@@ -165,8 +167,8 @@ class StubLoader {
165167

166168
if (configFile.existsSync()) {
167169
try {
168-
final json = jsonDecode(configFile.readAsStringSync())
169-
as Map<String, dynamic>;
170+
final json =
171+
jsonDecode(configFile.readAsStringSync()) as Map<String, dynamic>;
170172
final packages = json['packages'] as List<dynamic>?;
171173
if (packages == null) return null;
172174

test/commands/install_command_test.dart

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -782,8 +782,7 @@ void main() {
782782
cmd.arguments = parser.parse([]);
783783
await cmd.handle();
784784

785-
final content =
786-
File('${tempDir.path}/pubspec.yaml').readAsStringSync();
785+
final content = File('${tempDir.path}/pubspec.yaml').readAsStringSync();
787786

788787
expect(
789788
content,
@@ -797,8 +796,7 @@ void main() {
797796
await cmd.handle();
798797
await cmd.handle();
799798

800-
final content =
801-
File('${tempDir.path}/pubspec.yaml').readAsStringSync();
799+
final content = File('${tempDir.path}/pubspec.yaml').readAsStringSync();
802800
final count = '.env'.allMatches(content).length;
803801

804802
expect(
@@ -831,8 +829,7 @@ flutter:
831829
cmd.arguments = parser.parse([]);
832830
await cmd.handle();
833831

834-
final content =
835-
File('${tempDir.path}/pubspec.yaml').readAsStringSync();
832+
final content = File('${tempDir.path}/pubspec.yaml').readAsStringSync();
836833

837834
expect(content, contains('.env'));
838835
expect(content, contains('assets/images/'));
@@ -912,7 +909,8 @@ flutter:
912909
);
913910
});
914911

915-
test('graceful failure — install completes when download fails', () async {
912+
test('graceful failure — install completes when download fails',
913+
() async {
916914
downloadCmd.simulateDownloadSuccess = false;
917915
downloadCmd.arguments = downloadParser.parse([]);
918916

test/commands/key_generate_command_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ void main() {
1212
setUp(() {
1313
tempDir = Directory.systemTemp.createTempSync('magic_test_');
1414
Directory.current = tempDir;
15-
15+
1616
// Create a dummy pubspec.yaml to satisfy FileHelper.findProjectRoot()
1717
File('${tempDir.path}/pubspec.yaml').writeAsStringSync('name: test_app');
18-
18+
1919
command = KeyGenerateCommand();
2020
});
2121

test/commands/make_migration_command_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ void main() {
116116

117117
final migrationsDir =
118118
Directory('${tempDir.path}/lib/database/migrations');
119-
final filesAfterFirst = migrationsDir.listSync().whereType<File>().toList();
119+
final filesAfterFirst =
120+
migrationsDir.listSync().whereType<File>().toList();
120121
expect(filesAfterFirst.length, 1);
121122

122123
// 2. Tamper with the file to detect an accidental overwrite.

test/commands/make_model_command_test.dart

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void main() {
2727

2828
final file = File('${tempDir.path}/lib/app/models/monitor.dart');
2929
expect(file.existsSync(), isTrue);
30-
30+
3131
final content = file.readAsStringSync();
3232
expect(content, contains('class Monitor extends Model'));
3333
expect(content, contains("String get table => 'monitors'"));
@@ -40,7 +40,7 @@ void main() {
4040

4141
final file = File('${tempDir.path}/lib/app/models/category.dart');
4242
expect(file.existsSync(), isTrue);
43-
43+
4444
final content = file.readAsStringSync();
4545
expect(content, contains('class Category extends Model'));
4646
expect(content, contains("String get table => 'categories'"));
@@ -52,7 +52,7 @@ void main() {
5252

5353
final file = File('${tempDir.path}/lib/app/models/admin/profile.dart');
5454
expect(file.existsSync(), isTrue);
55-
55+
5656
final content = file.readAsStringSync();
5757
expect(content, contains('class Profile extends Model'));
5858
});
@@ -66,7 +66,7 @@ void main() {
6666

6767
final migDir = Directory('${tempDir.path}/lib/database/migrations');
6868
expect(migDir.existsSync(), isTrue);
69-
69+
7070
final files = migDir.listSync().whereType<File>().toList();
7171
expect(files.length, equals(1));
7272
expect(files.first.path, contains('_create_monitors_table.dart'));
@@ -79,20 +79,36 @@ void main() {
7979
final modelFile = File('${tempDir.path}/lib/app/models/monitor.dart');
8080
expect(modelFile.existsSync(), isTrue);
8181

82-
final ctrlFile = File('${tempDir.path}/lib/app/controllers/monitor_controller.dart');
82+
final ctrlFile =
83+
File('${tempDir.path}/lib/app/controllers/monitor_controller.dart');
8384
expect(ctrlFile.existsSync(), isTrue);
8485
});
8586

86-
test('--all flag creates model, migration, controller, factory, seeder, policy', () async {
87+
test(
88+
'--all flag creates model, migration, controller, factory, seeder, policy',
89+
() async {
8790
cmd.arguments = parser.parse(['Monitor', '--all']);
8891
await cmd.handle();
8992

90-
expect(File('${tempDir.path}/lib/app/models/monitor.dart').existsSync(), isTrue);
91-
expect(File('${tempDir.path}/lib/app/controllers/monitor_controller.dart').existsSync(), isTrue);
92-
expect(File('${tempDir.path}/lib/database/factories/monitor_factory.dart').existsSync(), isTrue);
93-
expect(File('${tempDir.path}/lib/database/seeders/monitor_seeder.dart').existsSync(), isTrue);
94-
expect(File('${tempDir.path}/lib/app/policies/monitor_policy.dart').existsSync(), isTrue);
95-
93+
expect(File('${tempDir.path}/lib/app/models/monitor.dart').existsSync(),
94+
isTrue);
95+
expect(
96+
File('${tempDir.path}/lib/app/controllers/monitor_controller.dart')
97+
.existsSync(),
98+
isTrue);
99+
expect(
100+
File('${tempDir.path}/lib/database/factories/monitor_factory.dart')
101+
.existsSync(),
102+
isTrue);
103+
expect(
104+
File('${tempDir.path}/lib/database/seeders/monitor_seeder.dart')
105+
.existsSync(),
106+
isTrue);
107+
expect(
108+
File('${tempDir.path}/lib/app/policies/monitor_policy.dart')
109+
.existsSync(),
110+
isTrue);
111+
96112
final migDir = Directory('${tempDir.path}/lib/database/migrations');
97113
final migFiles = migDir.listSync().whereType<File>().toList();
98114
expect(migFiles.length, equals(1));

test/console/command_test.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ void main() {
3939
test('arguments parsing', () async {
4040
final parser = ArgParser();
4141
cmd.configure(parser);
42-
43-
final results = parser.parse(['--name', 'test_user', '--force', 'pos_arg']);
42+
43+
final results =
44+
parser.parse(['--name', 'test_user', '--force', 'pos_arg']);
4445
cmd.arguments = results;
4546

4647
expect(cmd.option('name'), 'test_user');

test/console/generator_command_test.dart

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import 'dart:io';
55

66
class TestGeneratorCommand extends GeneratorCommand {
77
TestGeneratorCommand(this.testRoot);
8-
8+
99
final String testRoot;
1010

1111
@override
@@ -22,7 +22,7 @@ class TestGeneratorCommand extends GeneratorCommand {
2222

2323
@override
2424
Map<String, String> getReplacements(String name) => {};
25-
25+
2626
// Expose for testing without a real project root
2727
@override
2828
String getProjectRoot() => testRoot;
@@ -48,41 +48,44 @@ void main() {
4848
test('getPath resolves correctly', () {
4949
final path = cmd.getPath('Admin/UserController');
5050
expect(path, '${tempDir.path}/lib/test_dir/admin/user_controller.dart');
51-
51+
5252
final simplePath = cmd.getPath('UserController');
5353
expect(simplePath, '${tempDir.path}/lib/test_dir/user_controller.dart');
5454
});
5555

5656
test('buildClass replaces placeholders correctly', () {
5757
final content = cmd.buildClass('Admin/UserController');
58-
expect(content, 'class UserController {\n // test lib/test_dir/admin\n}');
58+
expect(
59+
content, 'class UserController {\n // test lib/test_dir/admin\n}');
5960
});
6061

6162
test('handle creates file successfully', () async {
6263
final results = parser.parse(['Admin/UserController']);
6364
cmd.arguments = results;
64-
65+
6566
await cmd.handle();
66-
67-
final file = File('${tempDir.path}/lib/test_dir/admin/user_controller.dart');
67+
68+
final file =
69+
File('${tempDir.path}/lib/test_dir/admin/user_controller.dart');
6870
expect(file.existsSync(), true);
69-
expect(file.readAsStringSync(), 'class UserController {\n // test lib/test_dir/admin\n}');
71+
expect(file.readAsStringSync(),
72+
'class UserController {\n // test lib/test_dir/admin\n}');
7073
});
7174

7275
test('handle aborts if file exists without --force', () async {
7376
// First run to create file
7477
var results = parser.parse(['UserController']);
7578
cmd.arguments = results;
7679
await cmd.handle();
77-
80+
7881
// Modify file to verify it's not overwritten
7982
final file = File('${tempDir.path}/lib/test_dir/user_controller.dart');
8083
file.writeAsStringSync('Modified');
81-
84+
8285
// Second run should fail
8386
results = parser.parse(['UserController']);
8487
cmd.arguments = results;
85-
88+
8689
// It won't throw, just print an error, but we can verify the file wasn't changed
8790
await cmd.handle();
8891
expect(file.readAsStringSync(), 'Modified');
@@ -93,17 +96,18 @@ void main() {
9396
var results = parser.parse(['UserController']);
9497
cmd.arguments = results;
9598
await cmd.handle();
96-
99+
97100
// Modify file
98101
final file = File('${tempDir.path}/lib/test_dir/user_controller.dart');
99102
file.writeAsStringSync('Modified');
100-
103+
101104
// Second run with --force should overwrite
102105
results = parser.parse(['UserController', '--force']);
103106
cmd.arguments = results;
104-
107+
105108
await cmd.handle();
106-
expect(file.readAsStringSync(), 'class UserController {\n // test lib/test_dir\n}');
109+
expect(file.readAsStringSync(),
110+
'class UserController {\n // test lib/test_dir\n}');
107111
});
108112
});
109113
}

test/console/kernel_test.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,29 +69,29 @@ void main() {
6969

7070
test('register and dispatch works', () async {
7171
kernel.register(cmd1);
72-
72+
7373
await kernel.handle(['make:test1']);
7474
expect(cmd1.wasHandled, true);
7575
});
7676

7777
test('registerMany works', () async {
7878
kernel.registerMany([cmd1, cmd2]);
79-
79+
8080
await kernel.handle(['make:test2', '--name', 'test']);
8181
expect(cmd2.wasHandled, true);
8282
expect(cmd2.option('name'), 'test');
8383
});
8484

8585
test('help prints correctly formatted text', () async {
8686
kernel.registerMany([cmd1, cmd2, rootCmd]);
87-
87+
8888
// We can't easily capture stdout without zone tricks,
8989
// but we can ensure it runs without errors for both empty args and --help
9090
await kernel.handle([]);
9191
await kernel.handle(['--help']);
9292
await kernel.handle(['-h']);
9393
});
94-
94+
9595
test('version prints without errors', () async {
9696
await kernel.handle(['--version']);
9797
await kernel.handle(['-V']);

0 commit comments

Comments
 (0)