mirror of
https://github.com/foss42/apidash.git
synced 2025-12-02 02:39:19 +08:00
Merge branch 'foss42:main' into enhance-auth-fields
This commit is contained in:
@@ -142,7 +142,7 @@ Future<
|
||||
// Process Results
|
||||
if (result.isError) {
|
||||
print("Pre-request script execution error: ${result.stringResult}");
|
||||
// Handle error - maybe show in UI, keep original request/env
|
||||
//TODO: Handle error - log this error in the logs console
|
||||
} else if (result.stringResult.isNotEmpty) {
|
||||
final resultMap = jsonDecode(result.stringResult);
|
||||
if (resultMap is Map<String, dynamic>) {
|
||||
@@ -153,7 +153,7 @@ Future<
|
||||
Map<String, Object?>.from(resultMap['request']));
|
||||
} catch (e) {
|
||||
print("Error deserializing modified request from script: $e");
|
||||
//TODO: Handle error - maybe keep original request?
|
||||
//TODO: Handle error - log this error in the logs console
|
||||
}
|
||||
}
|
||||
// Get Environment Modifications
|
||||
|
||||
@@ -58,6 +58,7 @@ Future<RequestModel> handlePreRequestScript(
|
||||
if (scriptResult.updatedEnvironment.isNotEmpty) {
|
||||
debugPrint(
|
||||
"Warning: Pre-request script updated environment variables, but no active environment was selected to save them to.");
|
||||
return requestModel;
|
||||
}
|
||||
return newRequestModel;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUp(() async {
|
||||
await testSetUpTempDirForHive();
|
||||
});
|
||||
@@ -627,4 +626,541 @@ void main() async {
|
||||
container.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
group('CollectionStateNotifier Scripting Tests', () {
|
||||
late ProviderContainer container;
|
||||
late CollectionStateNotifier notifier;
|
||||
|
||||
setUp(() {
|
||||
container = createContainer();
|
||||
notifier = container.read(collectionStateNotifierProvider.notifier);
|
||||
});
|
||||
|
||||
test('should update request with pre-request script', () {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
const preRequestScript = '''
|
||||
ad.request.headers.set('Authorization', 'Bearer ' + ad.environment.get('token'));
|
||||
ad.request.headers.set('X-Request-ID', 'req-' + Date.now());
|
||||
ad.console.log('Pre-request script executed');
|
||||
''';
|
||||
|
||||
notifier.update(id: id, preRequestScript: preRequestScript);
|
||||
|
||||
final updatedRequest = notifier.getRequestModel(id);
|
||||
expect(updatedRequest?.preRequestScript, equals(preRequestScript));
|
||||
});
|
||||
|
||||
test('should update request with post-response script', () {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
const postResponseScript = '''
|
||||
if (ad.response.status === 200) {
|
||||
const data = ad.response.json();
|
||||
if (data && data.token) {
|
||||
ad.environment.set('authToken', data.token);
|
||||
}
|
||||
}
|
||||
ad.console.log('Post-response script executed');
|
||||
''';
|
||||
|
||||
notifier.update(id: id, postRequestScript: postResponseScript);
|
||||
|
||||
final updatedRequest = notifier.getRequestModel(id);
|
||||
expect(updatedRequest?.postRequestScript, equals(postResponseScript));
|
||||
});
|
||||
|
||||
test('should preserve scripts when duplicating request', () {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
const preRequestScript = 'ad.console.log("Pre-request");';
|
||||
const postResponseScript = 'ad.console.log("Post-response");';
|
||||
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: preRequestScript,
|
||||
postRequestScript: postResponseScript,
|
||||
);
|
||||
notifier.duplicate(id: id);
|
||||
|
||||
final sequence = container.read(requestSequenceProvider);
|
||||
final duplicatedId = sequence.firstWhere((element) => element != id);
|
||||
final duplicatedRequest = notifier.getRequestModel(duplicatedId);
|
||||
|
||||
expect(duplicatedRequest?.preRequestScript, equals(preRequestScript));
|
||||
expect(duplicatedRequest?.postRequestScript, equals(postResponseScript));
|
||||
});
|
||||
|
||||
test('should clear scripts when set to empty strings', () {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
|
||||
// First add scripts
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: 'ad.console.log("test");',
|
||||
postRequestScript: 'ad.console.log("test");',
|
||||
);
|
||||
|
||||
// Then clear scripts
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: '',
|
||||
postRequestScript: '',
|
||||
);
|
||||
|
||||
final updatedRequest = notifier.getRequestModel(id);
|
||||
expect(updatedRequest?.preRequestScript, equals(''));
|
||||
expect(updatedRequest?.postRequestScript, equals(''));
|
||||
});
|
||||
|
||||
test('should preserve scripts when clearing response', () {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
const preRequestScript = 'ad.console.log("Pre-request");';
|
||||
const postResponseScript = 'ad.console.log("Post-response");';
|
||||
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: preRequestScript,
|
||||
postRequestScript: postResponseScript,
|
||||
);
|
||||
notifier.clearResponse(id: id);
|
||||
|
||||
final updatedRequest = notifier.getRequestModel(id);
|
||||
// Scripts should be preserved when clearing response
|
||||
expect(updatedRequest?.preRequestScript, equals(preRequestScript));
|
||||
expect(updatedRequest?.postRequestScript, equals(postResponseScript));
|
||||
});
|
||||
|
||||
test('should handle scripts with special characters and multi-line', () {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
const complexPreScript = '''
|
||||
// Pre-request script with special characters
|
||||
const apiKey = ad.environment.get('api-key');
|
||||
if (apiKey) {
|
||||
ad.request.headers.set('X-API-Key', apiKey);
|
||||
}
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
ad.request.headers.set('X-Timestamp', timestamp);
|
||||
|
||||
// Handle Unicode and special characters
|
||||
ad.request.headers.set('X-Test-Header', '测试_тест_テスト_🚀');
|
||||
ad.console.log('Complex pre-request script executed ✅');
|
||||
''';
|
||||
|
||||
const complexPostScript = '''
|
||||
// Post-response script with JSON parsing
|
||||
try {
|
||||
const data = ad.response.json();
|
||||
if (data && data.access_token) {
|
||||
ad.environment.set('token', data.access_token);
|
||||
ad.console.log('Token extracted: ' + data.access_token.substring(0, 10) + '...');
|
||||
}
|
||||
|
||||
// Handle different response codes
|
||||
if (ad.response.status >= 400) {
|
||||
ad.console.error('Request failed with status: ' + ad.response.status);
|
||||
ad.environment.set('lastError', 'HTTP ' + ad.response.status);
|
||||
} else {
|
||||
ad.environment.unset('lastError');
|
||||
}
|
||||
} catch (e) {
|
||||
ad.console.error('Script error: ' + e.message);
|
||||
}
|
||||
''';
|
||||
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: complexPreScript,
|
||||
postRequestScript: complexPostScript,
|
||||
);
|
||||
|
||||
final updatedRequest = notifier.getRequestModel(id);
|
||||
expect(updatedRequest?.preRequestScript, equals(complexPreScript));
|
||||
expect(updatedRequest?.postRequestScript, equals(complexPostScript));
|
||||
});
|
||||
|
||||
test('should handle empty and null scripts gracefully', () {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
|
||||
// Test with empty strings
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: '',
|
||||
postRequestScript: '',
|
||||
);
|
||||
|
||||
var updatedRequest = notifier.getRequestModel(id);
|
||||
expect(updatedRequest?.preRequestScript, equals(''));
|
||||
expect(updatedRequest?.postRequestScript, equals(''));
|
||||
|
||||
// Test with null values (should maintain existing values)
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: 'ad.console.log("test");',
|
||||
postRequestScript: 'ad.console.log("test");',
|
||||
);
|
||||
|
||||
updatedRequest = notifier.getRequestModel(id);
|
||||
expect(
|
||||
updatedRequest?.preRequestScript, equals('ad.console.log("test");'));
|
||||
expect(
|
||||
updatedRequest?.postRequestScript, equals('ad.console.log("test");'));
|
||||
});
|
||||
|
||||
test('should save and load scripts correctly', () async {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
const preRequestScript = '''
|
||||
ad.request.headers.set('Authorization', 'Bearer test-token');
|
||||
ad.environment.set('requestStartTime', Date.now().toString());
|
||||
''';
|
||||
const postResponseScript = '''
|
||||
const data = ad.response.json();
|
||||
if (data && data.user_id) {
|
||||
ad.environment.set('currentUserId', data.user_id);
|
||||
}
|
||||
''';
|
||||
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: preRequestScript,
|
||||
postRequestScript: postResponseScript,
|
||||
);
|
||||
await notifier.saveData();
|
||||
|
||||
// Create new container and load data
|
||||
late ProviderContainer newContainer;
|
||||
try {
|
||||
newContainer = ProviderContainer();
|
||||
final newNotifier =
|
||||
newContainer.read(collectionStateNotifierProvider.notifier);
|
||||
|
||||
// Give some time for the microtask in the constructor to complete
|
||||
await Future.delayed(const Duration(milliseconds: 10));
|
||||
|
||||
final loadedRequest = newNotifier.getRequestModel(id);
|
||||
|
||||
expect(loadedRequest?.preRequestScript, equals(preRequestScript));
|
||||
expect(loadedRequest?.postRequestScript, equals(postResponseScript));
|
||||
} finally {
|
||||
newContainer.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
test('should handle scripts in addRequestModel', () {
|
||||
const preRequestScript = 'ad.console.log("Added request pre-script");';
|
||||
const postResponseScript = 'ad.console.log("Added request post-script");';
|
||||
|
||||
final httpRequestModel = HttpRequestModel(
|
||||
method: HTTPVerb.post,
|
||||
url: 'https://api.apidash.dev/data',
|
||||
headers: const [
|
||||
NameValueModel(name: 'Content-Type', value: 'application/json'),
|
||||
],
|
||||
body: '{"test": true}',
|
||||
);
|
||||
|
||||
// Since addRequestModel takes HttpRequestModel, we'll test scripts through update
|
||||
notifier.addRequestModel(httpRequestModel, name: 'Test Request');
|
||||
|
||||
final sequence = container.read(requestSequenceProvider);
|
||||
final addedId = sequence.first;
|
||||
|
||||
notifier.update(
|
||||
id: addedId,
|
||||
preRequestScript: preRequestScript,
|
||||
postRequestScript: postResponseScript,
|
||||
);
|
||||
|
||||
final addedRequest = notifier.getRequestModel(addedId);
|
||||
expect(addedRequest?.preRequestScript, equals(preRequestScript));
|
||||
expect(addedRequest?.postRequestScript, equals(postResponseScript));
|
||||
});
|
||||
|
||||
test('should handle scripts with various JavaScript syntax', () {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
|
||||
const advancedPreScript = r'''
|
||||
// Advanced JavaScript features
|
||||
const config = {
|
||||
apiUrl: ad.environment.get('baseUrl') || 'https://api.apidash.dev/',
|
||||
timeout: 5000,
|
||||
retries: 3
|
||||
};
|
||||
|
||||
// Arrow functions and template literals
|
||||
const generateId = () => 'req_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// Destructuring and modern syntax
|
||||
const apiUrl = config.apiUrl;
|
||||
const timeout = config.timeout;
|
||||
ad.request.url.set(apiUrl + '/v1/users');
|
||||
ad.request.headers.set('X-Request-ID', generateId());
|
||||
ad.request.headers.set('X-Timeout', timeout.toString());
|
||||
|
||||
// Conditional logic
|
||||
if (ad.environment.has('debugMode')) {
|
||||
ad.request.headers.set('X-Debug', 'true');
|
||||
ad.console.log('Debug mode enabled');
|
||||
}
|
||||
|
||||
// Array operations
|
||||
const requiredHeaders = ['Authorization', 'Content-Type'];
|
||||
requiredHeaders.forEach(function(header) {
|
||||
if (!ad.request.headers.has(header)) {
|
||||
ad.console.warn('Missing required header: ' + header);
|
||||
}
|
||||
});
|
||||
''';
|
||||
|
||||
const advancedPostScript = r'''
|
||||
// Advanced response processing
|
||||
try {
|
||||
const response = ad.response.json();
|
||||
|
||||
// Object destructuring
|
||||
const responseData = response ? response.data : null;
|
||||
const meta = response ? response.meta : null;
|
||||
const errors = response ? response.errors : null;
|
||||
|
||||
if (errors && Array.isArray(errors)) {
|
||||
errors.forEach(function(error, index) {
|
||||
ad.console.error('Error ' + (index + 1) + ': ' + error.message);
|
||||
});
|
||||
ad.environment.set('hasErrors', 'true');
|
||||
} else {
|
||||
ad.environment.unset('hasErrors');
|
||||
}
|
||||
|
||||
// Handle pagination metadata
|
||||
if (meta && meta.pagination) {
|
||||
const page = meta.pagination.page;
|
||||
const total = meta.pagination.total;
|
||||
const hasNext = meta.pagination.hasNext;
|
||||
ad.environment.set('currentPage', page.toString());
|
||||
ad.environment.set('totalRecords', total.toString());
|
||||
ad.environment.set('hasNextPage', hasNext.toString());
|
||||
}
|
||||
|
||||
// Extract nested data
|
||||
if (responseData && Array.isArray(responseData)) {
|
||||
const activeItems = responseData.filter(function(item) {
|
||||
return item.status === 'active';
|
||||
});
|
||||
ad.environment.set('activeItemCount', activeItems.length.toString());
|
||||
|
||||
// Store first active item ID if available
|
||||
if (activeItems.length > 0) {
|
||||
ad.environment.set('firstActiveId', activeItems[0].id);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (parseError) {
|
||||
ad.console.error('Failed to parse response JSON: ' + parseError.message);
|
||||
ad.environment.set('parseError', parseError.message);
|
||||
}
|
||||
|
||||
// Response timing analysis
|
||||
const responseTime = ad.response.time;
|
||||
if (responseTime) {
|
||||
ad.environment.set('lastResponseTime', responseTime.toString());
|
||||
|
||||
if (responseTime > 2000) {
|
||||
ad.console.warn('Slow response detected: ' + responseTime + 'ms');
|
||||
ad.environment.set('slowResponse', 'true');
|
||||
} else {
|
||||
ad.environment.unset('slowResponse');
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: advancedPreScript,
|
||||
postRequestScript: advancedPostScript,
|
||||
);
|
||||
|
||||
final updatedRequest = notifier.getRequestModel(id);
|
||||
expect(updatedRequest?.preRequestScript, equals(advancedPreScript));
|
||||
expect(updatedRequest?.postRequestScript, equals(advancedPostScript));
|
||||
});
|
||||
|
||||
test(
|
||||
'should handle script updates without affecting other request properties',
|
||||
() {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
|
||||
// First set up a complete request
|
||||
notifier.update(
|
||||
id: id,
|
||||
method: HTTPVerb.post,
|
||||
url: 'https://api.apidash.dev/test',
|
||||
headers: const [
|
||||
NameValueModel(name: 'Content-Type', value: 'application/json'),
|
||||
NameValueModel(name: 'Accept', value: 'application/json'),
|
||||
],
|
||||
body: '{"test": "data"}',
|
||||
name: 'Test Request',
|
||||
description: 'A test request with scripts',
|
||||
);
|
||||
|
||||
final beforeRequest = notifier.getRequestModel(id);
|
||||
|
||||
// Now update only scripts
|
||||
const newPreScript = 'ad.console.log("Updated pre-script");';
|
||||
const newPostScript = 'ad.console.log("Updated post-script");';
|
||||
|
||||
notifier.update(
|
||||
id: id,
|
||||
preRequestScript: newPreScript,
|
||||
postRequestScript: newPostScript,
|
||||
);
|
||||
|
||||
final afterRequest = notifier.getRequestModel(id);
|
||||
|
||||
// Verify scripts were updated
|
||||
expect(afterRequest?.preRequestScript, equals(newPreScript));
|
||||
expect(afterRequest?.postRequestScript, equals(newPostScript));
|
||||
|
||||
// Verify other properties were preserved
|
||||
expect(afterRequest?.httpRequestModel?.method,
|
||||
equals(beforeRequest?.httpRequestModel?.method));
|
||||
expect(afterRequest?.httpRequestModel?.url,
|
||||
equals(beforeRequest?.httpRequestModel?.url));
|
||||
expect(afterRequest?.httpRequestModel?.headers,
|
||||
equals(beforeRequest?.httpRequestModel?.headers));
|
||||
expect(afterRequest?.httpRequestModel?.body,
|
||||
equals(beforeRequest?.httpRequestModel?.body));
|
||||
expect(afterRequest?.name, equals(beforeRequest?.name));
|
||||
expect(afterRequest?.description, equals(beforeRequest?.description));
|
||||
});
|
||||
|
||||
test(
|
||||
'should not modify original state during script execution - only execution copy',
|
||||
() {
|
||||
final id = notifier.state!.entries.first.key;
|
||||
|
||||
const preRequestScript = r'''
|
||||
// Script that modifies request properties
|
||||
ad.request.headers.set('X-Script-Modified', 'true');
|
||||
ad.request.headers.set('Authorization', 'Bearer script-token');
|
||||
ad.request.url.set('https://api.apidash.dev/');
|
||||
ad.request.params.set('scriptParam', 'scriptValue');
|
||||
ad.environment.set('scriptExecuted', 'true');
|
||||
ad.console.log('Pre-request script executed and modified request');
|
||||
''';
|
||||
|
||||
// Set up initial request properties
|
||||
notifier.update(
|
||||
id: id,
|
||||
method: HTTPVerb.get,
|
||||
url: 'https://api.apidash.dev/api',
|
||||
headers: const [
|
||||
NameValueModel(name: 'Content-Type', value: 'application/json'),
|
||||
NameValueModel(name: 'Accept', value: 'application/json'),
|
||||
],
|
||||
params: const [
|
||||
NameValueModel(name: 'originalParam', value: 'originalValue'),
|
||||
],
|
||||
preRequestScript: preRequestScript,
|
||||
);
|
||||
|
||||
// Capture the original state before script execution simulation
|
||||
final originalRequest = notifier.getRequestModel(id);
|
||||
final originalHttpRequestModel = originalRequest!.httpRequestModel!;
|
||||
|
||||
// Test the script execution isolation by simulating the copyWith pattern used in sendRequest
|
||||
final executionRequestModel = originalRequest.copyWith();
|
||||
|
||||
// Verify that the execution copy is separate from original
|
||||
expect(executionRequestModel.id, equals(originalRequest.id));
|
||||
expect(executionRequestModel.httpRequestModel?.url,
|
||||
equals(originalRequest.httpRequestModel?.url));
|
||||
expect(executionRequestModel.httpRequestModel?.headers,
|
||||
equals(originalRequest.httpRequestModel?.headers));
|
||||
expect(executionRequestModel.httpRequestModel?.params,
|
||||
equals(originalRequest.httpRequestModel?.params));
|
||||
|
||||
// Simulate script modifications on the execution copy
|
||||
final modifiedExecutionModel = executionRequestModel.copyWith(
|
||||
httpRequestModel: executionRequestModel.httpRequestModel?.copyWith(
|
||||
url: 'https://api.apidash.dev/',
|
||||
headers: [
|
||||
...originalHttpRequestModel.headers ?? [],
|
||||
const NameValueModel(name: 'X-Script-Modified', value: 'true'),
|
||||
const NameValueModel(
|
||||
name: 'Authorization', value: 'Bearer script-token'),
|
||||
],
|
||||
params: [
|
||||
...originalHttpRequestModel.params ?? [],
|
||||
const NameValueModel(name: 'scriptParam', value: 'scriptValue'),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
// Verify the execution copy has been modified
|
||||
expect(modifiedExecutionModel.httpRequestModel?.url,
|
||||
equals('https://api.apidash.dev/'));
|
||||
expect(
|
||||
modifiedExecutionModel.httpRequestModel?.headers?.length, equals(4));
|
||||
|
||||
final hasScriptModifiedHeader = modifiedExecutionModel
|
||||
.httpRequestModel?.headers
|
||||
?.any((header) => header.name == 'X-Script-Modified') ??
|
||||
false;
|
||||
expect(hasScriptModifiedHeader, isTrue);
|
||||
|
||||
final hasAuthHeader = modifiedExecutionModel.httpRequestModel?.headers
|
||||
?.any((header) => header.name == 'Authorization') ??
|
||||
false;
|
||||
expect(hasAuthHeader, isTrue);
|
||||
|
||||
final hasScriptParam = modifiedExecutionModel.httpRequestModel?.params
|
||||
?.any((param) => param.name == 'scriptParam') ??
|
||||
false;
|
||||
expect(hasScriptParam, isTrue);
|
||||
|
||||
// Verify that the original request in the state remains completely unchanged
|
||||
final currentRequest = notifier.getRequestModel(id);
|
||||
|
||||
expect(currentRequest?.httpRequestModel?.url,
|
||||
equals('https://api.apidash.dev/api'));
|
||||
expect(currentRequest?.httpRequestModel?.headers?.length, equals(2));
|
||||
expect(currentRequest?.httpRequestModel?.headers?[0].name,
|
||||
equals('Content-Type'));
|
||||
expect(currentRequest?.httpRequestModel?.headers?[0].value,
|
||||
equals('application/json'));
|
||||
expect(
|
||||
currentRequest?.httpRequestModel?.headers?[1].name, equals('Accept'));
|
||||
expect(currentRequest?.httpRequestModel?.headers?[1].value,
|
||||
equals('application/json'));
|
||||
expect(currentRequest?.httpRequestModel?.params?.length, equals(1));
|
||||
expect(currentRequest?.httpRequestModel?.params?[0].name,
|
||||
equals('originalParam'));
|
||||
expect(currentRequest?.httpRequestModel?.params?[0].value,
|
||||
equals('originalValue'));
|
||||
|
||||
// Verify no script-modified headers are present in the original state
|
||||
final hasScriptModifiedHeaderInOriginal = currentRequest
|
||||
?.httpRequestModel?.headers
|
||||
?.any((header) => header.name == 'X-Script-Modified') ??
|
||||
false;
|
||||
expect(hasScriptModifiedHeaderInOriginal, isFalse);
|
||||
|
||||
final hasAuthHeaderInOriginal = currentRequest?.httpRequestModel?.headers
|
||||
?.any((header) => header.name == 'Authorization') ??
|
||||
false;
|
||||
expect(hasAuthHeaderInOriginal, isFalse);
|
||||
|
||||
// Verify no script-modified params are present in the original state
|
||||
final hasScriptParamInOriginal = currentRequest?.httpRequestModel?.params
|
||||
?.any((param) => param.name == 'scriptParam') ??
|
||||
false;
|
||||
expect(hasScriptParamInOriginal, isFalse);
|
||||
|
||||
// Verify the script is preserved in the original
|
||||
expect(currentRequest?.preRequestScript, equals(preRequestScript));
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
container.dispose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
1736
test/utils/pre_post_script_utils_test.dart
Normal file
1736
test/utils/pre_post_script_utils_test.dart
Normal file
File diff suppressed because it is too large
Load Diff
341
test/widgets/editor_code_test.dart
Normal file
341
test/widgets/editor_code_test.dart
Normal file
@@ -0,0 +1,341 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_highlight/themes/xcode.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_code_editor/flutter_code_editor.dart';
|
||||
import 'package:flutter_highlight/themes/monokai.dart';
|
||||
import 'package:highlight/languages/javascript.dart';
|
||||
import 'package:highlight/languages/json.dart';
|
||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:apidash/widgets/editor_code.dart';
|
||||
import '../test_consts.dart';
|
||||
|
||||
void main() {
|
||||
group('CodeEditor Widget Tests', () {
|
||||
late CodeController testController;
|
||||
|
||||
setUp(() {
|
||||
testController = CodeController(
|
||||
text: 'print("Hello World")',
|
||||
language: javascript,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('renders CodeEditor with default parameters', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CodeEditor), findsOneWidget);
|
||||
expect(find.byType(CodeField), findsOneWidget);
|
||||
expect(find.byType(CodeTheme), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders CodeEditor in read-only mode', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor ReadOnly Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
readOnly: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CodeEditor), findsOneWidget);
|
||||
|
||||
// Verify the CodeField is in read-only mode
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
expect(codeField.readOnly, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('renders CodeEditor in editable mode by default',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Editable Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CodeEditor), findsOneWidget);
|
||||
|
||||
// Verify the CodeField is editable by default
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
expect(codeField.readOnly, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor has correct decoration properties', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Decoration Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
final decoration = codeField.decoration as BoxDecoration;
|
||||
|
||||
// Check border radius
|
||||
expect(decoration.borderRadius, equals(kBorderRadius8));
|
||||
|
||||
// Check that decoration has a border and color
|
||||
expect(decoration.border, isNotNull);
|
||||
expect(decoration.color, isNotNull);
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor has correct gutter style properties',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Gutter Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
final gutterStyle = codeField.gutterStyle;
|
||||
|
||||
expect(gutterStyle.width, equals(0));
|
||||
expect(gutterStyle.margin, equals(2));
|
||||
expect(gutterStyle.textAlign, equals(TextAlign.left));
|
||||
expect(gutterStyle.showFoldingHandles, isFalse);
|
||||
expect(gutterStyle.showLineNumbers, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor has correct smart typing settings', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Smart Typing Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
|
||||
expect(codeField.smartDashesType, equals(SmartDashesType.enabled));
|
||||
expect(codeField.smartQuotesType, equals(SmartQuotesType.enabled));
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor expands to fill available space', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Expand Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
expect(codeField.expands, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor uses correct text style with theme font size',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Text Style Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
final textStyle = codeField.textStyle;
|
||||
|
||||
// Verify that text style is based on kCodeStyle
|
||||
expect(textStyle?.fontFamily, equals(kCodeStyle.fontFamily));
|
||||
|
||||
// Verify that font size is taken from theme
|
||||
final themeContext = tester.element(find.byType(CodeEditor));
|
||||
final expectedFontSize =
|
||||
Theme.of(themeContext).textTheme.bodyMedium?.fontSize;
|
||||
expect(textStyle?.fontSize, equals(expectedFontSize));
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor uses theme colors for background and cursor',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Colors Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
final themeContext = tester.element(find.byType(CodeEditor));
|
||||
final colorScheme = Theme.of(themeContext).colorScheme;
|
||||
|
||||
// Verify background color
|
||||
expect(codeField.background, equals(colorScheme.surfaceContainerLowest));
|
||||
|
||||
// Verify cursor color
|
||||
expect(codeField.cursorColor, equals(colorScheme.primary));
|
||||
|
||||
// Verify decoration background color
|
||||
final decoration = codeField.decoration as BoxDecoration;
|
||||
expect(decoration.color, equals(colorScheme.surfaceContainerLowest));
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor with custom controller content', (tester) async {
|
||||
final customController = CodeController(
|
||||
text: '{\n "key": "value",\n "number": 42\n}',
|
||||
language: json,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Custom Content Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: customController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CodeEditor), findsOneWidget);
|
||||
|
||||
// Verify the controller is properly set
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
expect(codeField.controller, equals(customController));
|
||||
expect(codeField.controller.text, contains('"key": "value"'));
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor with all parameters set', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor All Parameters Test',
|
||||
theme: kThemeDataDark,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
readOnly: true,
|
||||
isDark: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CodeEditor), findsOneWidget);
|
||||
|
||||
final codeField = tester.widget<CodeField>(find.byType(CodeField));
|
||||
final codeTheme = tester.widget<CodeTheme>(find.byType(CodeTheme));
|
||||
|
||||
// Verify all parameters are applied
|
||||
expect(codeField.readOnly, isTrue);
|
||||
expect(codeTheme.data?.styles, equals(monokaiTheme));
|
||||
expect(codeField.controller, equals(testController));
|
||||
});
|
||||
|
||||
testWidgets('CodeEditor widget key is properly set', (tester) async {
|
||||
const testKey = Key('test-code-editor');
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Key Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
key: testKey,
|
||||
controller: testController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(testKey), findsOneWidget);
|
||||
expect(find.byType(CodeEditor), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders CodeEditor with light theme (isDark: false)',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Light Theme Test',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
isDark: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CodeEditor), findsOneWidget);
|
||||
expect(find.byType(CodeField), findsOneWidget);
|
||||
|
||||
// Verify the CodeTheme is using light theme (xcode)
|
||||
final codeTheme = tester.widget<CodeTheme>(find.byType(CodeTheme));
|
||||
expect(codeTheme.data?.styles, equals(xcodeTheme));
|
||||
});
|
||||
|
||||
testWidgets('renders CodeEditor with dark theme (isDark: true)',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'CodeEditor Dark Theme Test',
|
||||
theme: kThemeDataDark,
|
||||
home: Scaffold(
|
||||
body: CodeEditor(
|
||||
controller: testController,
|
||||
isDark: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CodeEditor), findsOneWidget);
|
||||
expect(find.byType(CodeField), findsOneWidget);
|
||||
|
||||
// Verify the CodeTheme is using dark theme (monokai)
|
||||
final codeTheme = tester.widget<CodeTheme>(find.byType(CodeTheme));
|
||||
expect(codeTheme.data?.styles, equals(monokaiTheme));
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user