Merge pull request 'fastercwtch' (#351) from fastercwtch into gherkin

Reviewed-on: #351
This commit is contained in:
Sarah Jamie Lewis 2022-02-02 22:13:41 +00:00
commit 8d81116f2d
23 changed files with 148 additions and 54 deletions

View File

@ -1,4 +0,0 @@
@env:aliceandbob1
Feature: Streamer mode setting hides onions
Scenario: Turning on streamer mode
Given

View File

@ -1,3 +1,4 @@
@env:persist
Feature: Basic Profile Management
Scenario: Error on Creating a Profile without a Display Name
Given I wait until the widget with type 'ProfileMgrView' is present
@ -18,14 +19,18 @@ Feature: Basic Profile Management
Then I tap the "passwordCheckBox" widget
And I expect the text 'New Password' to be absent
And I take a screenshot
Then I fill the "displayNameFormElement" field with "Alice (<h1>hello</h1>)"
Then I fill the "displayNameFormElement" field with "Alice (Unencrypted)"
Then I tap the "button" widget with label "Add new profile"
And I expect a "ProfileRow" widget with text "Alice (<h1>hello</h1>)"
And I expect a "ProfileRow" widget with text "Alice (Unencrypted)"
And I take a screenshot
Then I tap the "ProfileRow" widget with label "Alice (<h1>hello</h1>)"
And I expect the text 'Alice (<h1>hello</h1>) » Conversations' to be present
Then I tap the "ProfileRow" widget with label "Alice (Unencrypted)"
And I expect the text "Alice (Unencrypted) » Conversations" to be present
And I take a screenshot
Scenario: Load Unencrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Alice (Unencrypted)"
Scenario: Create Encrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the button with tooltip "Add new profile"
@ -41,4 +46,45 @@ Feature: Basic Profile Management
And I take a screenshot
Then I tap the "ProfileRow" widget with label "Alice (Encrypted)"
And I expect the text 'Alice (Encrypted) » Conversations' to be present
And I take a screenshot
And I take a screenshot
Scenario: Load an Encrypted Profile by Unlocking it with a Password
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Alice (Encrypted)"
Scenario: Load an Encrypted Profile by Unlocking it with a Password and Change the Name
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Alice (Encrypted)"
When I tap the "IconButton" widget with tooltip "Edit Profile Alice (Encrypted)"
Then I expect the text 'Display Name' to be present
Then I fill the "displayNameFormElement" field with "Carol (Encrypted)"
And I tap the "button" widget with label "Save Profile"
And I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)"
Scenario: Delete an Encrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect the text 'Enter a password to view your profiles' to be absent
And I tap the button with tooltip "Unlock encrypted profiles by entering their password."
Then I expect the text 'Enter a password to view your profiles' to be present
When I fill the "unlockPasswordProfileElement" field with "password1"
And I tap the "button" widget with label "Unlock"
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)"
And I take a screenshot
When I tap the "IconButton" widget with tooltip "Edit Profile Carol (Encrypted)"
Then I expect the text 'Display Name' to be present
When I tap the button that contains the text "Delete"
Then I expect the text "Really Delete Profile" to be present
When I tap the "button" widget with label "Really Delete Profile"
And I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Carol (Encrypted)" to be absent

View File

@ -38,7 +38,9 @@ void main() {
// overrides
TapWidgetWithType(),
TapWidgetWithLabel(),
TapWidgetWithTooltip(),
ExpectWidgetWithText(),
AbsentWidgetWithText(),
WaitUntilTypeExists(),
ExpectTextToBePresent(),
ExpectWidgetWithTextWithin(),

View File

@ -37,6 +37,22 @@ StepDefinitionGeneric TapWidgetWithLabel() {
firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first);
//print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.scrollIntoView(finder);
await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric TapWidgetWithTooltip() {
return given2<String, String, FlutterWorld>(
RegExp(r'I tap the {string} widget with tooltip {string}$'),
(ofType, text, context) async {
final finder = context.world.appDriver.findByDescendant(
context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type),
context.world.appDriver.findBy(text, FindType.tooltip),
firstMatchOnly: true);
await context.world.appDriver.scrollIntoView(finder);
await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle();
},
@ -59,6 +75,23 @@ StepDefinitionGeneric ExpectWidgetWithText() {
);
}
StepDefinitionGeneric AbsentWidgetWithText() {
return given2<String, String, FlutterWorld>(
RegExp(r'I expect a {string} widget with text {string} to be absent$'),
(ofType, text, context) async {
final finder = context.world.appDriver.findByDescendant(
context.world.appDriver.findBy(widgetTypeByName(ofType), FindType.type),
context.world.appDriver.findBy(text, FindType.text),
firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first);
//print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.isAbsent(finder);
await context.world.appDriver.waitForAppToSettle();
},
);
}
StepDefinitionGeneric TapButtonWithText() {
return given1<String, FlutterWorld>(
RegExp(r'I tap the {string} (?:button|element|label|icon|field|text|widget)$'),
@ -67,8 +100,7 @@ StepDefinitionGeneric TapButtonWithText() {
context.world.appDriver.findBy(Flwtch, FindType.type),
context.world.appDriver.findBy(input1, FindType.key),
firstMatchOnly: true);
//Text wdg = await context.world.appDriver.widget(finder, ExpectedWidgetResultType.first);
//print(wdg.debugDescribeChildren().first.)
await context.world.appDriver.scrollIntoView(finder);
await context.world.appDriver.tap(finder);
await context.world.appDriver.waitForAppToSettle();
},
@ -229,6 +261,8 @@ Type widgetTypeByName(String input1) {
return TorIcon;
case "button":
return ElevatedButton;
case "IconButton":
return IconButton;
case "ProfileRow":
return ProfileRow;
default:

View File

@ -1,6 +1,6 @@
{
"@@locale": "de",
"@@last_modified": "2022-01-18T00:38:14+01:00",
"@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",

View File

@ -1,6 +1,7 @@
{
"@@locale": "en",
"@@last_modified": "2022-01-18T00:38:14+01:00",
"@@last_modified": "2022-01-28T19:57:41+01:00",
"editProfile": "Edit Profile",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",
@ -239,7 +240,6 @@
"radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password",
"copiedToClipboardNotification": "Copied to Clipboard",
"editProfile": "Edit Profille",
"newProfile": "New Profile",
"defaultProfileName": "Alice",
"profileName": "Display name",

View File

@ -1,6 +1,6 @@
{
"@@locale": "es",
"@@last_modified": "2022-01-18T00:38:14+01:00",
"@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",

View File

@ -1,22 +1,24 @@
{
"@@locale": "fr",
"@@last_modified": "2022-01-18T00:38:14+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",
"torSettingsCustomControlPortDescription": "Use a custom port for control connections to the Tor proxy",
"torSettingsCustomControlPort": "Custom Control Port",
"torSettingsCustomSocksPortDescription": "Use a custom port for data connections to the Tor proxy",
"torSettingsCustomSocksPort": "Custom SOCKS Port",
"torSettingsEnabledAdvancedDescription": "Use an existing Tor service on your system, or change the parameters of the Cwtch Tor Service",
"torSettingsEnabledAdvanced": "Enable Advanced Tor Configuration",
"@@last_modified": "2022-01-28T19:57:41+01:00",
"editProfile": "Modifier le profil",
"settingTheme": "Utilisez des thèmes clairs",
"torSettingsUseCustomTorServiceConfiguration": "Utiliser une configuration personnalisée du service Tor (torrc)",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Remplacer la configuration par défaut de tor. Avertissement : Cela peut être dangereux. Ne l'activez que si vous savez ce que vous faites.",
"torSettingsErrorSettingPort": "Le numéro de port doit être compris entre 1 et 65535.",
"torSettingsEnableCache": "Cache du Consensus Tor",
"torSettingsEnabledCacheDescription": "Mettre en cache le consensus Tor actuellement téléchargé pour le réutiliser la prochaine fois que Cwtch est ouvert. Cela permettra à Tor de démarrer plus rapidement. Lorsqu'il est désactivé, Cwtch purgera les données mises en cache au démarrage.",
"torSettingsEnabledAdvancedDescription": "Utilisez un service Tor existant sur votre système ou modifiez les paramètres du service Tor de Cwtch.",
"torSettingsEnabledAdvanced": "Activer la configuration avancée de Tor",
"torSettingsCustomSocksPortDescription": "Utiliser un port personnalisé pour les connexions de données au proxy Tor",
"torSettingsCustomSocksPort": "Port SOCKS personnalisé",
"torSettingsCustomControlPortDescription": "Utiliser un port personnalisé pour contrôler les connexions au proxy Tor",
"torSettingsCustomControlPort": "Port de contrôle personnalisé",
"labelTorNetwork": "Réseau Tor",
"labelACNCircuitInfo": "Informations sur le circuit ACN",
"fileSharingSettingsDownloadFolderTooltip": "Parcourir pour sélectionner un autre dossier par défaut pour les fichiers téléchargés.",
"fileSharingSettingsDownloadFolderDescription": "Lorsque les fichiers sont téléchargés automatiquement (par exemple, les fichiers image, lorsque les aperçus d'image sont activés), un emplacement par défaut pour télécharger les fichiers est nécessaire.",
"descriptionACNCircuitInfo": "Informations détaillées sur le chemin que le réseau de communication anonyme utilise pour se connecter à cette conversation.",
"msgConfirmSend": "Êtes-vous sûr de vouloir envoyer",
"acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe de",
"msgFileTooBig": "La taille du fichier ne peut pas dépasser 10 Go",
@ -187,7 +189,6 @@
"deleteConfirmLabel": "Tapez SUPPRIMER pour confirmer",
"addNewProfileBtn": "Ajouter un nouveau profil",
"enterProfilePassword": "Entrez un mot de passe pour consulter vos profils",
"editProfile": "Modifier le profil",
"radioUsePassword": "Mot de passe",
"radioNoPassword": "Non chiffré (pas de mot de passe)",
"saveProfileBtn": "Sauvegarder le profil",
@ -205,7 +206,6 @@
"localePt": "Portugais",
"localeDe": "Allemand",
"settingInterfaceZoom": "Niveau de zoom",
"settingTheme": "Thème",
"themeLight": "Clair",
"themeDark": "Sombre",
"experimentsEnabled": "Activer les expériences",

View File

@ -1,6 +1,6 @@
{
"@@locale": "it",
"@@last_modified": "2022-01-18T00:38:14+01:00",
"@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",

View File

@ -1,6 +1,6 @@
{
"@@locale": "pl",
"@@last_modified": "2022-01-18T00:38:14+01:00",
"@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",

View File

@ -1,6 +1,6 @@
{
"@@locale": "pt",
"@@last_modified": "2022-01-18T00:38:14+01:00",
"@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",
@ -227,7 +227,7 @@
"radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password",
"copiedToClipboardNotification": "Copiado",
"editProfile": "Edit Profille",
"editProfile": "Edit Profile",
"newProfile": "New Profile",
"defaultProfileName": "Alice",
"profileName": "Display name",

View File

@ -1,6 +1,6 @@
{
"@@locale": "ru",
"@@last_modified": "2022-01-18T00:38:14+01:00",
"@@last_modified": "2022-01-28T19:57:41+01:00",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",

View File

@ -37,7 +37,7 @@ Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
print("runApp()");
runApp(Flwtch());
sleep(Duration(seconds:1));
sleep(Duration(seconds: 1));
}
class Flwtch extends StatefulWidget {

View File

@ -34,7 +34,7 @@ class RemoteServerInfoState extends ChangeNotifier {
if (status == "Authenticated") {
// syncing, set lastPreSyncMessageTime
_groups.forEach((g) {
if(g.lastMessageTime.isAfter(lastPreSyncMessagTime)) {
if (g.lastMessageTime.isAfter(lastPreSyncMessagTime)) {
lastPreSyncMessagTime = g.lastMessageTime;
}
});
@ -52,4 +52,4 @@ class RemoteServerInfoState extends ChangeNotifier {
}
List<ContactInfoState> get groups => _groups.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
}
}

View File

@ -183,7 +183,11 @@ ThemeData mkThemeData(Settings opaque) {
scrollbarTheme: ScrollbarThemeData(isAlwaysShown: false, thumbColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor)),
tabBarTheme: TabBarTheme(indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor))),
dialogTheme: DialogTheme(
backgroundColor: opaque.current().backgroundPaneColor, titleTextStyle: TextStyle(color: opaque.current().mainTextColor), contentTextStyle: TextStyle(color: opaque.current().mainTextColor)),
backgroundColor: opaque.current().backgroundPaneColor,
titleTextStyle: TextStyle(color: opaque.current().mainTextColor),
contentTextStyle: TextStyle(
color: opaque.current().mainTextColor,
)),
textTheme: TextTheme(
headline1: TextStyle(color: opaque.current().mainTextColor),
headline2: TextStyle(color: opaque.current().mainTextColor),

View File

@ -186,13 +186,13 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
autoFillHints: [AutofillHints.newPassword],
validator: (value) {
// Password field can be empty when just updating the profile, not on creation
if (Provider.of<ProfileInfoState>(context).isEncrypted &&
if (Provider.of<ProfileInfoState>(context, listen: false).isEncrypted &&
Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty &&
value.isEmpty &&
usePassword) {
return AppLocalizations.of(context)!.passwordErrorEmpty;
}
if (Provider.of<ErrorHandler>(context).deleteProfileError == true) {
if (Provider.of<ErrorHandler>(context, listen: false).deleteProfileError == true) {
return AppLocalizations.of(context)!.enterCurrentPasswordForDelete;
}
return null;

View File

@ -163,7 +163,7 @@ class _ContactsViewState extends State<ContactsView> {
builder: (context, child) => RepaintBoundary(child: ContactRow()),
);
});
final divided = ListTile.divideTiles(
context: context,
tiles: tiles,

View File

@ -107,7 +107,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
items: themes.keys.map<DropdownMenuItem<String>>((String themeId) {
return DropdownMenuItem<String>(
value: themeId,
child: Text("ddi_$themeId",key: Key("ddi_$themeId")),//getThemeName(context, themeId)),
child: Text("ddi_$themeId", key: Key("ddi_$themeId")), //getThemeName(context, themeId)),
);
}).toList()),
leading: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor),

View File

@ -108,7 +108,12 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
}
// Global Settings
actions.add(IconButton(key: Key("OpenSettingsView"), icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.tooltipOpenSettings, splashRadius: Material.defaultSplashRadius / 2, onPressed: _pushGlobalSettings));
actions.add(IconButton(
key: Key("OpenSettingsView"),
icon: Icon(Icons.settings),
tooltip: AppLocalizations.of(context)!.tooltipOpenSettings,
splashRadius: Material.defaultSplashRadius / 2,
onPressed: _pushGlobalSettings));
// shutdown cwtch
actions.add(IconButton(icon: Icon(Icons.close), tooltip: AppLocalizations.of(context)!.shutdownCwtchTooltip, splashRadius: Material.defaultSplashRadius / 2, onPressed: _modalShutdown));
@ -191,6 +196,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
height: 20,
),
CwtchPasswordField(
key: Key("unlockPasswordProfileElement"),
autofocus: true,
controller: ctrlrPassword,
action: unlock,

View File

@ -5,7 +5,16 @@ import 'package:provider/provider.dart';
// Provides a styled Text Field for use in Form Widgets.
// Callers must provide a text controller, label helper text and a validator.
class CwtchButtonTextField extends StatefulWidget {
CwtchButtonTextField({required this.controller, required this.onPressed, required this.icon, required this.tooltip, this.readonly = true, this.labelText, this.testKey, this.onChanged,});
CwtchButtonTextField({
required this.controller,
required this.onPressed,
required this.icon,
required this.tooltip,
this.readonly = true,
this.labelText,
this.testKey,
this.onChanged,
});
final TextEditingController controller;
final Function()? onPressed;
final Function(String)? onChanged;

View File

@ -80,7 +80,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
Widget wdgIcons = Platform.isAndroid
? SizedBox.shrink()
: Visibility(
visible: true,//Provider.of<AppState>(context).hoveredIndex == Provider.of<MessageMetadata>(context).messageID,
visible: true, //Provider.of<AppState>(context).hoveredIndex == Provider.of<MessageMetadata>(context).messageID,
maintainSize: true,
maintainAnimation: true,
maintainState: true,

View File

@ -89,10 +89,7 @@ class _ProfileRowState extends State<ProfileRow> {
builder: (BuildContext buildcontext) {
return OrientationBuilder(builder: (orientationBuilderContext, orientation) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList)
],
providers: [ChangeNotifierProvider<ProfileInfoState>.value(value: profile), ChangeNotifierProvider<ContactListState>.value(value: profile.contactList)],
builder: (innercontext, widget) {
var appState = Provider.of<AppState>(context);
var settings = Provider.of<Settings>(context);