* Add dialect options to support localisation

* Add test for ParseFeatures support for localisation
Этот коммит содержится в:
GrindStone 2024-11-14 16:42:18 +07:00 коммит произвёл GitHub
родитель c5a88f62c2
коммит da4633a421
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
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
Просмотреть файл

@ -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",