Localisation support (#665)
* Add dialect options to support localisation * Add test for ParseFeatures support for localisation
Этот коммит содержится в:
родитель
c5a88f62c2
коммит
da4633a421
5 изменённых файлов: 162 добавлений и 23 удалений
|
@ -48,6 +48,9 @@ type Options struct {
|
|||
// from feature files
|
||||
Tags string
|
||||
|
||||
// Dialect to be used to parse feature files. If not set, default to "en".
|
||||
Dialect string
|
||||
|
||||
// The formatter name
|
||||
Format string
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ func ExtractFeaturePathLine(p string) (string, int) {
|
|||
return retPath, line
|
||||
}
|
||||
|
||||
func parseFeatureFile(fsys fs.FS, path string, newIDFunc func() string) (*models.Feature, error) {
|
||||
func parseFeatureFile(fsys fs.FS, path, dialect string, newIDFunc func() string) (*models.Feature, error) {
|
||||
reader, err := fsys.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -42,7 +42,7 @@ func parseFeatureFile(fsys fs.FS, path string, newIDFunc func() string) (*models
|
|||
defer reader.Close()
|
||||
|
||||
var buf bytes.Buffer
|
||||
gherkinDocument, err := gherkin.ParseGherkinDocument(io.TeeReader(reader, &buf), newIDFunc)
|
||||
gherkinDocument, err := gherkin.ParseGherkinDocumentForLanguage(io.TeeReader(reader, &buf), dialect, newIDFunc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s - %v", path, err)
|
||||
}
|
||||
|
@ -54,11 +54,11 @@ func parseFeatureFile(fsys fs.FS, path string, newIDFunc func() string) (*models
|
|||
return &f, nil
|
||||
}
|
||||
|
||||
func parseBytes(path string, feature []byte, newIDFunc func() string) (*models.Feature, error) {
|
||||
func parseBytes(path string, feature []byte, dialect string, newIDFunc func() string) (*models.Feature, error) {
|
||||
reader := bytes.NewReader(feature)
|
||||
|
||||
var buf bytes.Buffer
|
||||
gherkinDocument, err := gherkin.ParseGherkinDocument(io.TeeReader(reader, &buf), newIDFunc)
|
||||
gherkinDocument, err := gherkin.ParseGherkinDocumentForLanguage(io.TeeReader(reader, &buf), dialect, newIDFunc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s - %v", path, err)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func parseBytes(path string, feature []byte, newIDFunc func() string) (*models.F
|
|||
return &f, nil
|
||||
}
|
||||
|
||||
func parseFeatureDir(fsys fs.FS, dir string, newIDFunc func() string) ([]*models.Feature, error) {
|
||||
func parseFeatureDir(fsys fs.FS, dir, dialect string, newIDFunc func() string) ([]*models.Feature, error) {
|
||||
var features []*models.Feature
|
||||
return features, fs.WalkDir(fsys, dir, func(p string, f fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
|
@ -85,7 +85,7 @@ func parseFeatureDir(fsys fs.FS, dir string, newIDFunc func() string) ([]*models
|
|||
return nil
|
||||
}
|
||||
|
||||
feat, err := parseFeatureFile(fsys, p, newIDFunc)
|
||||
feat, err := parseFeatureFile(fsys, p, dialect, newIDFunc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func parseFeatureDir(fsys fs.FS, dir string, newIDFunc func() string) ([]*models
|
|||
})
|
||||
}
|
||||
|
||||
func parsePath(fsys fs.FS, path string, newIDFunc func() string) ([]*models.Feature, error) {
|
||||
func parsePath(fsys fs.FS, path, dialect string, newIDFunc func() string) ([]*models.Feature, error) {
|
||||
var features []*models.Feature
|
||||
|
||||
path, line := ExtractFeaturePathLine(path)
|
||||
|
@ -114,10 +114,10 @@ func parsePath(fsys fs.FS, path string, newIDFunc func() string) ([]*models.Feat
|
|||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
return parseFeatureDir(fsys, path, newIDFunc)
|
||||
return parseFeatureDir(fsys, path, dialect, newIDFunc)
|
||||
}
|
||||
|
||||
ft, err := parseFeatureFile(fsys, path, newIDFunc)
|
||||
ft, err := parseFeatureFile(fsys, path, dialect, newIDFunc)
|
||||
if err != nil {
|
||||
return features, err
|
||||
}
|
||||
|
@ -146,14 +146,18 @@ func parsePath(fsys fs.FS, path string, newIDFunc func() string) ([]*models.Feat
|
|||
}
|
||||
|
||||
// ParseFeatures ...
|
||||
func ParseFeatures(fsys fs.FS, filter string, paths []string) ([]*models.Feature, error) {
|
||||
func ParseFeatures(fsys fs.FS, filter, dialect string, paths []string) ([]*models.Feature, error) {
|
||||
var order int
|
||||
|
||||
if dialect == "" {
|
||||
dialect = gherkin.DefaultDialect
|
||||
}
|
||||
|
||||
featureIdxs := make(map[string]int)
|
||||
uniqueFeatureURI := make(map[string]*models.Feature)
|
||||
newIDFunc := (&messages.Incrementing{}).NewId
|
||||
for _, path := range paths {
|
||||
feats, err := parsePath(fsys, path, newIDFunc)
|
||||
feats, err := parsePath(fsys, path, dialect, newIDFunc)
|
||||
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
|
@ -189,14 +193,18 @@ func ParseFeatures(fsys fs.FS, filter string, paths []string) ([]*models.Feature
|
|||
|
||||
type FeatureContent = flags.Feature
|
||||
|
||||
func ParseFromBytes(filter string, featuresInputs []FeatureContent) ([]*models.Feature, error) {
|
||||
func ParseFromBytes(filter, dialect string, featuresInputs []FeatureContent) ([]*models.Feature, error) {
|
||||
var order int
|
||||
|
||||
if dialect == "" {
|
||||
dialect = gherkin.DefaultDialect
|
||||
}
|
||||
|
||||
featureIdxs := make(map[string]int)
|
||||
uniqueFeatureURI := make(map[string]*models.Feature)
|
||||
newIDFunc := (&messages.Incrementing{}).NewId
|
||||
for _, f := range featuresInputs {
|
||||
ft, err := parseBytes(f.Name, f.Contents, newIDFunc)
|
||||
ft, err := parseBytes(f.Name, f.Contents, dialect, newIDFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ Feature: eat godogs
|
|||
{Name: "MyCoolDuplicatedFeature", Contents: []byte(eatGodogContents)},
|
||||
}
|
||||
|
||||
featureFromBytes, err := parser.ParseFromBytes("", input)
|
||||
featureFromBytes, err := parser.ParseFromBytes("", "", input)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, featureFromBytes, 1)
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ Feature: eat godogs
|
|||
},
|
||||
}
|
||||
|
||||
featureFromFile, err := parser.ParseFeatures(fsys, "", []string{baseDir})
|
||||
featureFromFile, err := parser.ParseFeatures(fsys, "", "", []string{baseDir})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, featureFromFile, 1)
|
||||
|
||||
|
@ -88,7 +88,7 @@ Feature: eat godogs
|
|||
{Name: filepath.Join(baseDir, featureFileName), Contents: []byte(eatGodogContents)},
|
||||
}
|
||||
|
||||
featureFromBytes, err := parser.ParseFromBytes("", input)
|
||||
featureFromBytes, err := parser.ParseFromBytes("", "", input)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, featureFromBytes, 1)
|
||||
|
||||
|
@ -155,7 +155,7 @@ func Test_ParseFeatures_FromMultiplePaths(t *testing.T) {
|
|||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
features, err := parser.ParseFeatures(test.fsys, "", test.paths)
|
||||
features, err := parser.ParseFeatures(test.fsys, "", "", test.paths)
|
||||
if test.expError != nil {
|
||||
require.Error(t, err)
|
||||
require.EqualError(t, err, test.expError.Error())
|
||||
|
@ -178,3 +178,132 @@ func Test_ParseFeatures_FromMultiplePaths(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ParseFeatures_Localisation(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
dialect string
|
||||
contents string
|
||||
}{
|
||||
"english": {
|
||||
dialect: "en",
|
||||
contents: `
|
||||
Feature: dummy
|
||||
Rule: dummy
|
||||
Background: dummy
|
||||
Given dummy
|
||||
When dummy
|
||||
Then dummy
|
||||
Scenario: dummy
|
||||
Given dummy
|
||||
When dummy
|
||||
Then dummy
|
||||
And dummy
|
||||
But dummy
|
||||
Example: dummy
|
||||
Given dummy
|
||||
When dummy
|
||||
Then dummy
|
||||
Scenario Outline: dummy
|
||||
Given dummy
|
||||
When dummy
|
||||
Then dummy
|
||||
`,
|
||||
},
|
||||
"afrikaans": {
|
||||
dialect: "af",
|
||||
contents: `
|
||||
Funksie: dummy
|
||||
Regel: dummy
|
||||
Agtergrond: dummy
|
||||
Gegewe dummy
|
||||
Wanneer dummy
|
||||
Dan dummy
|
||||
Voorbeeld: dummy
|
||||
Gegewe dummy
|
||||
Wanneer dummy
|
||||
Dan dummy
|
||||
En dummy
|
||||
Maar dummy
|
||||
Voorbeelde: dummy
|
||||
Gegewe dummy
|
||||
Wanneer dummy
|
||||
Dan dummy
|
||||
Situasie Uiteensetting: dummy
|
||||
Gegewe dummy
|
||||
Wanneer dummy
|
||||
Dan dummy
|
||||
`,
|
||||
},
|
||||
"arabic": {
|
||||
dialect: "ar",
|
||||
contents: `
|
||||
خاصية: dummy
|
||||
Rule: dummy
|
||||
الخلفية: dummy
|
||||
بفرض dummy
|
||||
متى dummy
|
||||
اذاً dummy
|
||||
مثال: dummy
|
||||
بفرض dummy
|
||||
متى dummy
|
||||
اذاً dummy
|
||||
و dummy
|
||||
لكن dummy
|
||||
امثلة: dummy
|
||||
بفرض dummy
|
||||
متى dummy
|
||||
اذاً dummy
|
||||
سيناريو مخطط: dummy
|
||||
بفرض dummy
|
||||
متى dummy
|
||||
اذاً dummy
|
||||
`,
|
||||
},
|
||||
"chinese simplified": {
|
||||
dialect: "zh-CN",
|
||||
contents: `
|
||||
功能: dummy
|
||||
规则: dummy
|
||||
背景: dummy
|
||||
假如 dummy
|
||||
当 dummy
|
||||
那么 dummy
|
||||
场景: dummy
|
||||
假如 dummy
|
||||
当 dummy
|
||||
那么 dummy
|
||||
而且 dummy
|
||||
但是 dummy
|
||||
例子: dummy
|
||||
假如 dummy
|
||||
当 dummy
|
||||
那么 dummy
|
||||
场景大纲: dummy
|
||||
假如 dummy
|
||||
当 dummy
|
||||
那么 dummy
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
featureFileName := "godogs.feature"
|
||||
baseDir := "base"
|
||||
|
||||
for name, test := range tests {
|
||||
test := test
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fsys := fstest.MapFS{
|
||||
filepath.Join(baseDir, featureFileName): {
|
||||
Data: []byte(test.contents),
|
||||
Mode: fs.FileMode(0o644),
|
||||
},
|
||||
}
|
||||
|
||||
featureTestDialect, err := parser.ParseFeatures(fsys, "", test.dialect, []string{baseDir})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, featureTestDialect, 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
6
run.go
6
run.go
|
@ -247,7 +247,7 @@ func runWithOptions(suiteName string, runner runner, opt Options) int {
|
|||
opt.FS = storage.FS{FS: opt.FS}
|
||||
|
||||
if len(opt.FeatureContents) > 0 {
|
||||
features, err := parser.ParseFromBytes(opt.Tags, opt.FeatureContents)
|
||||
features, err := parser.ParseFromBytes(opt.Tags, opt.Dialect, opt.FeatureContents)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return exitOptionError
|
||||
|
@ -256,7 +256,7 @@ func runWithOptions(suiteName string, runner runner, opt Options) int {
|
|||
}
|
||||
|
||||
if len(opt.Paths) > 0 {
|
||||
features, err := parser.ParseFeatures(opt.FS, opt.Tags, opt.Paths)
|
||||
features, err := parser.ParseFeatures(opt.FS, opt.Tags, opt.Dialect, opt.Paths)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return exitOptionError
|
||||
|
@ -389,7 +389,7 @@ func (ts TestSuite) RetrieveFeatures() ([]*models.Feature, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return parser.ParseFeatures(opt.FS, opt.Tags, opt.Paths)
|
||||
return parser.ParseFeatures(opt.FS, opt.Tags, opt.Dialect, opt.Paths)
|
||||
}
|
||||
|
||||
func getDefaultOptions() (*Options, error) {
|
||||
|
|
|
@ -494,7 +494,7 @@ func (tc *godogFeaturesScenario) theLoggedMessagesShouldInclude(ctx context.Cont
|
|||
}
|
||||
|
||||
func (tc *godogFeaturesScenario) followingStepsShouldHave(status string, steps *DocString) error {
|
||||
var expected = strings.Split(steps.Content, "\n")
|
||||
expected := strings.Split(steps.Content, "\n")
|
||||
var actual, unmatched, matched []string
|
||||
|
||||
storage := tc.testedSuite.storage
|
||||
|
@ -673,7 +673,7 @@ func (tc *godogFeaturesScenario) featurePath(path string) {
|
|||
}
|
||||
|
||||
func (tc *godogFeaturesScenario) parseFeatures() error {
|
||||
fts, err := parser.ParseFeatures(storage.FS{}, "", tc.paths)
|
||||
fts, err := parser.ParseFeatures(storage.FS{}, "", "", tc.paths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1226,7 +1226,6 @@ func TestTestSuite_Run(t *testing.T) {
|
|||
s.Step("^multistep has ambiguous$", func() Steps {
|
||||
return Steps{"step is ambiguous"}
|
||||
})
|
||||
|
||||
},
|
||||
Options: &Options{
|
||||
Format: "pretty",
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче