This commit is contained in:
Rob Prouse 2023-01-08 10:47:59 -05:00
Родитель 7b67ca6d48
Коммит f316ed469a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: DA234FE8BF92BF0C
8 изменённых файлов: 11 добавлений и 554 удалений

Просмотреть файл

@ -60,9 +60,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="NUnit2FrameworkProvider.cs" />
<Compile Include="NUnit3FrameworkProvider.cs" />
<Compile Include="NUnit2SolutionManager.cs" />
<Compile Include="NUnit3SolutionManager.cs" />
<Compile Include="NUnitUnitTestClassManager.cs" />
<Compile Include="NUnitUnitTestProjectManager.cs" />

Просмотреть файл

@ -1,59 +0,0 @@
// ***********************************************************************
// Copyright (c) 2015-2018 Terje Sandstrom
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************
using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.TestPlatform.TestGeneration.Data;
using Microsoft.VisualStudio.TestPlatform.TestGeneration.Model;
namespace TestGeneration.Extensions.NUnit
{
/// <summary>
/// The provider for the NUnit 2 unit test framework.
/// </summary>
[Export(typeof(IFrameworkProvider))]
public class NUnit2FrameworkProvider : FrameworkProviderBase
{
/// <summary>
/// Initializes a new instance of the <see cref="NUnit2FrameworkProvider"/> class.
/// </summary>
/// <param name="serviceProvider">The service provider to use to get the interfaces required.</param>
/// <param name="configurationSettings">The configuration settings object to be used to determine how the test method is generated.</param>
/// <param name="naming">The naming object used to decide how projects, classes and methods are named and created.</param>
/// <param name="directory">The directory object to use for directory operations.</param>
[ImportingConstructor]
public NUnit2FrameworkProvider(IServiceProvider serviceProvider, IConfigurationSettings configurationSettings, INaming naming, IDirectory directory)
: base(new NUnit2SolutionManager(serviceProvider, naming, directory), new NUnitUnitTestProjectManager(serviceProvider, naming), new NUnitUnitTestClassManager(configurationSettings, naming))
{
}
/// <summary>
/// Gets the name of the provider.
/// </summary>
public override string Name => "NUnit2";
/// <summary>
/// Gets the name of the assembly.
/// </summary>
public override string AssemblyName => "nunit.framework";
}
}

Просмотреть файл

@ -1,83 +0,0 @@
// ***********************************************************************
// Copyright (c) 2015-2018 Terje Sandstrom
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************
using System;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.TestPlatform.TestGeneration;
using Microsoft.VisualStudio.TestPlatform.TestGeneration.Data;
using Microsoft.VisualStudio.TestPlatform.TestGeneration.Logging;
using Microsoft.VisualStudio.TestPlatform.TestGeneration.Model;
using VSLangProj80;
namespace TestGeneration.Extensions.NUnit
{
/// <summary>
/// A solution manager for NUnit unit tests.
/// </summary>
public class NUnit2SolutionManager : SolutionManagerBase
{
private const string NUnitVersion = "2.6.4";
private const string NunitAdapterVersion = "2.2.0";
private const string TestSDKVersion = "17.4.1";
/// <summary>
/// Initializes a new instance of the <see cref="NUnit2SolutionManager"/> class.
/// </summary>
/// <param name="serviceProvider">The service provider to use to get the interfaces required.</param>
/// <param name="naming">The naming object used to decide how projects, classes and methods are named and created.</param>
/// <param name="directory">The directory object to use for directory operations.</param>
public NUnit2SolutionManager(IServiceProvider serviceProvider, INaming naming, IDirectory directory)
: base(serviceProvider, naming, directory)
{
}
/// <summary>
/// Performs any preparatory tasks that have to be done after a new unit test project has been created.
/// </summary>
/// <param name="unitTestProject">The <see cref="Project"/> of the unit test project that has just been created.</param>
/// <param name="sourceMethod">The <see cref="CodeFunction2"/> of the source method that is to be unit tested.</param>
protected override void OnUnitTestProjectCreated(Project unitTestProject, CodeFunction2 sourceMethod)
{
if (unitTestProject == null)
{
throw new ArgumentNullException(nameof(unitTestProject));
}
TraceLogger.LogInfo("NUnitSolutionManager.OnUnitTestProjectCreated: Adding reference to NUnit 2 assemblies through nuget.");
base.OnUnitTestProjectCreated(unitTestProject, sourceMethod);
EnsureNuGetReference(unitTestProject, "NUnit", NUnitVersion);
EnsureNuGetReference(unitTestProject, "NUnitTestAdapter", NunitAdapterVersion);
EnsureNuGetReference(unitTestProject, "Microsoft.NET.Test.Sdk", TestSDKVersion);
var vsp = unitTestProject.Object as VSProject2;
var reference = vsp?.References.Find(GlobalConstants.MSTestAssemblyName);
if (reference != null)
{
TraceLogger.LogInfo("NUnitSolutionManager.OnUnitTestProjectCreated: Removing reference to {0}", reference.Name);
reference.Remove();
}
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@ -55,7 +55,6 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="NUnit2TestFramework.cs" />
<Compile Include="NUnitAssertMethodFilter.cs" />
<Compile Include="NUnit3TestFramework.cs" />
<Compile Include="NUnitTestFrameworkMetadata.cs" />

Просмотреть файл

@ -1,396 +0,0 @@
// ***********************************************************************
// Copyright (c) 2015-2018 Terje Sandstrom
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.ExtendedReflection.Asserts;
using Microsoft.ExtendedReflection.Collections;
using Microsoft.ExtendedReflection.Metadata;
using Microsoft.ExtendedReflection.Metadata.Names;
using Microsoft.ExtendedReflection.Monitoring;
using Microsoft.ExtendedReflection.Utilities;
using Microsoft.ExtendedReflection.Utilities.Safe;
using Microsoft.ExtendedReflection.Utilities.Safe.Diagnostics;
using Microsoft.Pex.Engine.ComponentModel;
using Microsoft.Pex.Engine.TestFrameworks;
namespace TestGeneration.Extensions.IntelliTest.NUnit
{
/// <summary>
/// NUnit 2 test framework
/// </summary>
[Serializable]
sealed class NUnit2TestFramework : AttributeBasedTestFrameworkBase
{
private const string NUnitVersion = "2.6.4";
/// <summary>
/// Initializes a new instance of the <see cref="NUnit2TestFramework"/> class.
/// </summary>
/// <param name="host">
/// </param>
public NUnit2TestFramework(IPexComponent host)
: base(host)
{ }
/// <summary>
/// identify of the test framework
/// </summary>
/// <value></value>
public override string Name => "NUnit2";
/// <summary>
/// Gets the assembly name of the framework main's assembly. This name is used
/// to automatically discover test frameworks, based the assembly references
/// </summary>
/// <value></value>
public override ShortAssemblyName AssemblyName => NUnitTestFrameworkMetadata.AssemblyName;
/// <summary>
/// Gets the root namespace.
/// </summary>
/// <value>The root namespace.</value>
public override string RootNamespace => NUnitTestFrameworkMetadata.RootNamespace;
/// <summary>
/// The test framework references.
/// </summary>
public override ICountable<ShortReferenceAssemblyName> References =>
Indexable.One(new ShortReferenceAssemblyName(ShortAssemblyName.FromName("NUnit"), NUnitVersion, AssemblyReferenceType.NugetReference));
/// <summary>
/// The _directory.
/// </summary>
private string directory = null;
/// <summary>
/// Hint on the location of the test framework assembly
/// </summary>
/// <param name="pdirectory">
/// The directory.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public override bool TryGetDirectory(out string pdirectory)
{
if (directory == null)
{
var programFiles = new DirectoryInfo(Environment.ExpandEnvironmentVariables("%ProgramFiles%"));
var info = programFiles.GetDirectories("NUnit-Net-*", SearchOption.TopDirectoryOnly);
directory = info.Length == 0 ? string.Empty : Path.Combine(info[0].FullName, "bin");
}
pdirectory = directory;
return !SafeString.IsNullOrEmpty(directory);
}
/// <summary>
/// Gets a value indicating whether
/// partial test classes
/// </summary>
/// <value></value>
public override bool SupportsPartialClasses => true;
/// <summary>
/// The supports project bitness.
/// </summary>
/// <param name="bitness">
/// The bitness.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public override bool SupportsProjectBitness(Bitness bitness)
{
SafeDebug.Assume(bitness != Bitness.Unsupported, "bitness != Bitness.Unsupported");
return true;
}
/// <summary>
/// The _expected exception attribute.
/// </summary>
[NonSerialized]
TypeName expectedExceptionAttribute;
/// <summary>
/// Gets the ExpectedException attribute.
/// </summary>
/// <value>The expected exception attribute.</value>
public override TypeName ExpectedExceptionAttribute => expectedExceptionAttribute ??
(expectedExceptionAttribute = NUnitTestFrameworkMetadata.AttributeName("ExpectedException"));
/// <summary>
/// Tries the read expected exception.
/// </summary>
/// <param name="target">
/// The method.
/// </param>
/// <param name="exceptionType">
/// Type of the exception.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public override bool TryReadExpectedException(ICustomAttributeProviderEx target, out TypeEx exceptionType)
{
var attribute = AttributeHelper.GetAttribute(target, ExpectedExceptionAttribute);
if (attribute != null)
{
var attributeType = attribute.GetType();
// read exception type using reflection.
var field = attributeType.GetField("expectedException", BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
var t = field.GetValue(attribute) as Type;
bool isClass;
if (t != null && ReflectionHelper.TryGetIsClass(t, out isClass) && isClass
&& !ReflectionHelper.ContainsGenericParameters(t))
{
exceptionType = MetadataFromReflection.GetType(t);
return true;
}
}
}
exceptionType = null;
return false;
}
/// <summary>
/// Tries to get the assembly set up tear down attribute.
/// </summary>
/// <param name="assembly">
/// The assembly.
/// </param>
/// <param name="setUp">
/// The set up.
/// </param>
/// <param name="tearDown">
/// The tear down.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public override bool TryGetAssemblySetupTeardownMethods(AssemblyEx assembly, out Method setUp, out Method tearDown)
{
setUp = null;
tearDown = null;
return false;
}
/// <summary>
/// Gets a value indicating whether[fixture set up tear down are instance methods.
/// </summary>
/// <value>
/// <c>true</c> if [fixture set up tear down instance]; otherwise, <c>false</c>.
/// </value>
public override bool FixtureSetupTeardownInstance => true;
/// <summary>
/// The _fixture attribute.
/// </summary>
[NonSerialized]
TypeName fixtureAttribute;
/// <summary>
/// Gets the name of the fixture attribute
/// </summary>
/// <value>The fixture attribute.</value>
public override TypeName FixtureAttribute => fixtureAttribute ??
(fixtureAttribute = NUnitTestFrameworkMetadata.AttributeName("TestFixture"));
/// <summary>
/// The _fixture set up attribute.
/// </summary>
[NonSerialized]
TypeName fixtureSetUpAttribute;
/// <summary>
/// Gets the name of the fixture setup attribute
/// </summary>
/// <value>The fixture set up attribute.</value>
public override TypeName FixtureSetupAttribute => fixtureSetUpAttribute ??
(fixtureSetUpAttribute = NUnitTestFrameworkMetadata.AttributeName("TestFixtureSetUp"));
/// <summary>
/// The _fixture tear down attribute.
/// </summary>
[NonSerialized]
TypeName fixtureTearDownAttribute;
/// <summary>
/// Gets the name of the fixture teardown attribute
/// </summary>
/// <value>The fixture tear down attribute.</value>
public override TypeName FixtureTeardownAttribute => fixtureTearDownAttribute ??
(fixtureTearDownAttribute = NUnitTestFrameworkMetadata.AttributeName("TestFixtureTearDown"));
/// <summary>
/// The _set up attribute.
/// </summary>
[NonSerialized]
TypeName setUpAttribute;
/// <summary>
/// Gets the name of the test setup attribute.
/// </summary>
/// <value>The set up attribute.</value>
public override TypeName SetupAttribute => setUpAttribute ??
(setUpAttribute = NUnitTestFrameworkMetadata.AttributeName("SetUp"));
/// <summary>
/// The _test attribute.
/// </summary>
[NonSerialized]
TypeName testAttribute;
/// <summary>
/// Gets the name of the test attribute.
/// </summary>
/// <value>The set up attribute.</value>
public override TypeName TestAttribute => testAttribute ?? (testAttribute = NUnitTestFrameworkMetadata.AttributeName("Test"));
/// <summary>
/// The _tear down attribute.
/// </summary>
[NonSerialized]
TypeName tearDownAttribute;
/// <summary>
/// Gets the name of the test teardown attribute.
/// </summary>
/// <value>The tear down attribute.</value>
public override TypeName TeardownAttribute => tearDownAttribute ??
(tearDownAttribute = NUnitTestFrameworkMetadata.AttributeName("TearDown"));
/// <summary>
/// The _ignore attribute.
/// </summary>
[NonSerialized]
TypeName ignoreAttribute;
/// <summary>
/// Gets the ignore attribute.
/// </summary>
/// <value>The ignore attribute.</value>
public override TypeName IgnoreAttribute => ignoreAttribute ??
(ignoreAttribute = NUnitTestFrameworkMetadata.AttributeName("Ignore"));
/// <summary>
/// Whether the ignore attribute constructor takes a message as its first argument.
/// </summary>
/// <value></value>
protected override bool HasIgnoreAttributeMessage => true;
/// <summary>
/// Gets the ignore message property.
/// </summary>
/// <value>The ignore message property.</value>
protected override string IgnoreMessageProperty => "Reason";
/// <summary>
/// Gets the expected exception property name.
/// </summary>
/// <value>The expected exception property.</value>
protected override string ExpectedExceptionProperty => "ExceptionType";
/// <summary>
/// Gets a list of attribute that should be duplicated from the
/// pex test to the parameterized test
/// </summary>
/// <returns>
/// The <see cref="IEnumerable"/>.
/// </returns>
protected override IEnumerable<TypeName> GetSatelliteAttributeTypes()
{
return Indexable.Array(CategoryAttribute,
NUnitTestFrameworkMetadata.AttributeName("Description"),
NUnitTestFrameworkMetadata.AttributeName("Explicit"),
NUnitTestFrameworkMetadata.AttributeName("Platform"),
NUnitTestFrameworkMetadata.AttributeName("Property")
);
}
/// <summary>
/// The _category attribute.
/// </summary>
[NonSerialized]
TypeName categoryAttribute;
/// <summary>
/// Gets the category attribute.
/// </summary>
private TypeName CategoryAttribute => categoryAttribute ??
(categoryAttribute = NUnitTestFrameworkMetadata.AttributeName("Category"));
/// <summary>
/// Tries the get categories.
/// </summary>
/// <param name="element">
/// The element.
/// </param>
/// <param name="names">
/// The names.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
protected override bool TryGetCategories( ICustomAttributeProviderEx element, out IEnumerable<string> names)
{
SafeDebug.AssumeNotNull(element, "element");
// TODO
names = null;
return false;
}
/// <summary>
/// The _assertion exception type.
/// </summary>
[NonSerialized]
TypeName assertionExceptionType;
/// <summary>
/// Gets the type of the assertion exception.
/// </summary>
/// <value>The type of the assertion exception.</value>
public override TypeName AssertionExceptionType => assertionExceptionType ?? (assertionExceptionType = TypeDefinitionName.FromName(
NUnitTestFrameworkMetadata.AssemblyName,
-1,
false,
NUnitTestFrameworkMetadata.RootNamespace,
"AssertionException").SelfInstantiation);
/// <summary>
/// Gets a value indicating whether supports static test methods.
/// </summary>
public override bool SupportsStaticTestMethods => false;
/// <summary>
/// Gets the assert method filters.
/// </summary>
public override IIndexable<IAssertMethodFilter> AssertMethodFilters => Indexable.One<IAssertMethodFilter>(NUnitAssertMethodFilter.Instance);
}
}

Просмотреть файл

@ -1,4 +1,4 @@
// ***********************************************************************
// ***********************************************************************
// Copyright (c) 2015-2018 Terje Sandstrom
//
// Permission is hereby granted, free of charge, to any person obtaining
@ -36,9 +36,6 @@ namespace TestGeneration.Extensions.IntelliTest.NUnit
using Microsoft.Pex.Framework.Packages;
public class NunitTestFrameworkPackageAttribute : PexPackageAttributeBase
{
protected override void Initialize(IEngine engine)
@ -49,9 +46,7 @@ namespace TestGeneration.Extensions.IntelliTest.NUnit
var host = testFrameworkService as IPexComponent;
testFrameworkService.AddTestFramework(new NUnit3TestFramework(host));
testFrameworkService.AddTestFramework(new NUnit2TestFramework(host));
}
public override string Name => nameof(NUnitTestFrameworkPackage);
}

Просмотреть файл

@ -2,9 +2,9 @@
This is an extension for Visual Studio that extends
the test functionality to allow you to create unit tests and IntelliTests.
It works for Visual Studio 2015, 2017 and 2019.
It works for Visual Studio 2022.
The extension extends the built in test generator functionality allowing
developers to generate tests using NUnit 2.6.x or NUnit 3.x.
developers to generate tests using NUnit 3.x.
Please note that IntelliTest is only available in Visual
Studio Enterprise edition. Other editions of Visual Studio
@ -19,6 +19,9 @@ and searching for **Test Generator NUnit Extension**.
You can also download from the [GitHub Releases Page](https://github.com/nunit/nunit-vs-testgenerator/releases).
For VS 2022:
Awaiting release
For VS 2017/2019:
[Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.TestGeneratorNUnitextension-18371)
@ -28,6 +31,6 @@ For VS 2015:
## Documentation
Documentation and a Quick Start Guide can be found on the [NUnit Documentation Wiki](https://github.com/nunit/docs/wiki/Visual-Studio-Test-Generator).
Documentation and a Quick Start Guide can be found on the [NUnit Documentation Wiki](https://docs.nunit.org/articles/vs-test-generator/TestGenerator-Installation.html).
Release notes can be found at [NUnit TestGenerator Release Notes](https://github.com/nunit/docs/wiki/TestGenerator-Release-Notes)
Release notes can be found at [NUnit TestGenerator Release Notes](https://docs.nunit.org/articles/vs-test-generator/Visual-Studio-Test-Generator.html)

Просмотреть файл

@ -4,12 +4,12 @@
<Identity Id="TestGeneration.Extensions.NUnit.de03c1c9-a95a-4098-ac1b-7a66d50fae60" Version="3.0" Language="en-US" Publisher="Terje Sandstrom" />
<DisplayName>NUnit Test Generator VS2022</DisplayName>
<Description xml:space="preserve">Test Generator, NUnit extensions for Visual Studio.
Creates Unit tests and Intellitests with both NUnit 2.6.4 and NUnit 3 frameworks.
Creates Unit tests and Intellitests with the NUnit 3 frameworks.
Works on Visual Studio from version 2022.
</Description>
<MoreInfo>https://github.com/nunit/nunit-vs-testgenerator</MoreInfo>
<License>license.txt</License>
<GettingStartedGuide>https://github.com/nunit/docs/wiki/Visual-Studio-Test-Generator</GettingStartedGuide>
<GettingStartedGuide>https://docs.nunit.org/articles/vs-test-generator/Visual-Studio-Test-Generator.html</GettingStartedGuide>
<ReleaseNotes>https://github.com/nunit/docs/wiki/TestGenerator-Release-Notes</ReleaseNotes>
<Icon>nunit_90.png</Icon>
<PreviewImage>preview.jpg</PreviewImage>