Comment réaliser des tests unitaires sur l'input d'un composant Blazor ?

L'une des choses que je préfère avec Blazor, consiste dans la possibilité de tester intégralement un composant via bUnit. Un bon exemple valant toujours mieux que de long discourt, voici un cas simple permettant de résoudre deux problématiques :

  • Le besoin de modifier un input.
  • Le besoin de consulter le contenu d'un input.

Pour commencer, on peut initialiser un contexte de test, et effectuer un rendu d'un composant :


// Création du context de test 
using var context = new TestContext();

// Création du composent et rendu
var obj = context.RenderComponent<MonComposent>();


Pour modifier une input dont l'Id serait inputId, on peut utiliser le code :


obj.Find("#inputId").Change("Ma valeur");


Pour consulter le contenu d'un input dont l'ID serait inputId, on peut utiliser le code :


var actual  = obj.Find("#inputId").GetAttribute("value");


Pour finir, voici un exemple complet :


[Theory] 
[InlineData("Foo")] 
public void Exemple(String value) 
{
  // Création du context de test 
  using var context = new TestContext(); 

  // Création du composent et rendu 
  var obj = context.RenderComponent<MonComposent>(); 

  // Modification
  obj.Find("#inputId").Change(value); 

  // … ajouter des interactions avec le composant ici 

  // Vérifier la valeur de l'input 
  var actual= obj.Find("#inputId").GetAttribute("value"); 
  actual.Should().BeEquivalentTo(value);  
} 

Conclusion

Voici du code simple et efficace. Tout ce que j'aime!

Jérémy Jeanson

Comment passer des paramètres à une action deferred de Windows Installer ?

Dans un précédent article, je présentais la démarche à suivre pour exécuter une action custom avec des privilèges élevés. Cette approche passe par l'exécution de l'action dans un contexte deferred. Celui-ci a un impact sur la manière d'accéder aux propriétés via l'action custom.

Habituellement, WiX toolset permet d'utiliser la syntaxe qui suit pour accéder à des paramètres lors de l'installation. Dans le cadre d'une action custom deferred, le code suivant n'est pas utilisable.


[CustomAction] 
public static ActionResult DoSomething(Session session) 
{ 
  var destination = session["INSTALLFOLDER"]; 
  // … 
}

Il doit être remplacé par :


[CustomAction] 
public static ActionResult DoSomething(Session session) 
{ 
  var destination = session.CustomActionData["INSTALLFOLDER"]; 
  // … 
}

CustomActionData est alimenté via la propriété Value de l'action custom. Le contenu accepte une syntaxe du style nom1=valuer1;nom2=valeur2;....

Malheureusement, cette propriété ne peut pas être affectée directement. Il faut ajout une seconde action custom pour cela. Dans l'exemple suivant, j'ai créé une action custom SetDoSomethingValue qui affecter les propriétés que j'utilise dans l'action custom DoSomething.


<!-- Actions personnalisées --> 
<Binary Id="CustomActions" 
    SourceFile="CustomActions.CA.dll"/> 

<CustomAction Id="DoSomething" 
    BinaryRef="CustomActions" 
    DllEntry="DoSomething" 
    Execute="deferred" 
    Return="check" 
    Impersonate="no"/> 

<CustomAction Id="SetDoSomethingValue" 
    Property="DoSomething" 
    Value="INSTALLFOLDER=[INSTALLFOLDER]"/>

Ben évidemment, l'action custom SetDoSomethingValue doit être ajoutée à la section InstallExecuteSequence, et s'exécuter avant l'action DoSomething.


<InstallExecuteSequence>
  <Custom Action="DoSomething" Before="InstallFinalize" Condition="NOT REMOVE"/> 
  <Custom Action="SetDoSomethingValue" Before="DoSomething" Condition="NOT REMOVE"/> 
</InstallExecuteSequence>

Jérémy Jeanson

Comment exécuter une action custom Windows Installer avec des privilèges élevés ?

Par défaut, lors du déploiement, une action custom s'exécute avec les privilèges de l'utilisateur courant. Celle-ci ne dispose d'aucun privilège administrateur. Le fait de fixer un scope perMachine comme ceci ne suffit pas :


<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 
  <Package 
     Name="..." 
     Scope="perMachine"> 

Une solution peut consister dans le fait de d'utiliser un terminal avec une élévation de privilège pour lancer msiexec (msiexec /i mon-fichier.msi).

Heureusement WiX Toolset, permet de demander à Windows Installer d'exécuter une action custom avec des privilèges élever facilement. Pour cela, il faut utiliser l'option deferred pour la propriété Execute, et no pour la propriété Impersonate. La raison de ce changement est expluqée ici : Immediate Custom Actions Always Impersonate - Visual Studio Setup (microsoft.com)

Exemple :


<!-- Actions personnalisées --> 
<Binary 
    Id="CustomActions" 
    SourceFile="CustomActions.CA.dll"/> 
<CustomAction Id="DoSomething" 
    BinaryRef="CustomActions" 
    DllEntry="DoSomething" 
    Return="check" 
    Execute="deferred" 
    Impersonate="no"/>

Bien évidemment, l'action custom DoSomething doit être présente dans la séquence InstallExecuteSequence. Son exécution doit être planifiée entre InstallExecute, et InstallFinalize.


<InstallExecuteSequence>
  <Custom Action="DoSomething" Before="InstallFinalize" /> 
</InstallExecuteSequence> 

Après avoir appliqué ces modifications, l'action custom s'exécutera toujours avec des privilèges élevés.

Jérémy Jeanson

Comment automatiser l’installation de MSI avec Azure DevOps, et Release Manager ?

Automatisé le déploiement de MSI, n’a jamais été une tâche triviale. Aujourd’hui, je vous propose d’effectuer celle-ci avec Azure DevOps.

Pour l’exemple, j’utilise un pipeline de Release, et j’y ai ajouté une tâche powerShell :

Tâche PowerShell d'un pipeline de release

Dans la propriété Inline, il suffit de coller le contenu du script qui suit, et le tour est joué :


$msi = Get-ChildItem $(System.ArtifactsDirectory)\*.msi -Recurse
Write-Host "Msi trouvé : $msi"

$log = "D:\Applications\MyApp\installation.log"
if (Test-Path $log) {
        Remove-Item $log -verbose
}
 
$arguments = @( "/i", $msi, "INSTALLFOLDER=""D:\Applications\MyApp""", "/quiet","/lv",$log)
 Write-Host $arguments 
 
Start-Process msiexec.exe -ArgumentList $arguments -Wait

Ce script va piloter msiexec en mode silencieux (c’est-à-dire sans interface), et attendre la fin du déploiement. Pour diagnostiquer les éventuels problèmes qui pourraient survenir lors du déploiement, un fichier de log est créé. Ce fichier est conservé jusqu’à la prochaine exécution.

Bien évidemment, il faudra adapter la variable $arguments en fonction de vos besoins, et les différents chemins.

Ce script peut aussi être utilisé avec un pipeline de type multistage, et un job de déploiement.

Jérémy Jeanson

Comment adapter le nom d'un job d'Azure Pipeline, en fonction d'un paramètre de build ?

Avec Azure Pipeline, changer le nom d'un job à la voler, ou du moins afficher un libellé propre n'est pas toujours de tout repos. Certaines syntaxes peuvent fonctionner avec la version Cloud d'Azure DevOps, et ne pas être prises en charge on-premise. D'autres ne sont pas possibles sur certaines propriétés.

Afin d'arriver à mes fins avec Azure DevOps, j'ai mis en place une stratégie simple, et efficace : passer par des variables.

Quelle que soit la plateforme :

  • On peut conditionner la création d'une variable.
  • On peut conditionner l'affectation d'une variable (via une tache powershell par exemple).
  • Toutes les propriétés acceptent des variables.

Voici un petit exemple avec la propriété displayName d'un job (un vrai cauchemar à conditionner). Le texte affiché pour présenter le job change en fonction d'un paramètre choisi par l'utilisateur au moment de lancer la build. Pour effectuer cette opération, je passe par une variable jobDisplayName dont la définition change en fonction du paramètre jobType.


parameters:
  - name: jobType
    displayName: Type de build
    type: string
    default: evolution
    values:
      - correctif
      - evolution

variables:
  ${{ if eq(parameters.jobType,'correctif') }}:
    jobDisplayName: "Compilation d'un correctif"
  ${{ else }}:
    jobDisplayName: "Compilation d'une nouvelle version"

jobs:
- job: Build
  displayName: ${{ variables.jobDisplayName }}

Simple, efficace, et déclinable à volonté.

Attention : le else utilisé ici ne fonctionnera pas sur une instance Azure DevOps Server qui n'est pas à jour. Il faudra alors utiliser un second if.

Jérémy Jeanson